/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ /* * This code is based on IBXM mod player * * Copyright (c) 2015, Martin Cameron * All rights reserved. * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the * following conditions are met: * * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * * Redistributions in binary form must reproduce the * above copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * * Neither the name of the organization nor the names of * its contributors may be used to endorse or promote * products derived from this software without specific * prior written permission. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #include "common/debug.h" #include "common/file.h" #include "common/memstream.h" #include "audio/audiostream.h" #include "audio/mods/mod_xm_s3m.h" #include "audio/mods/module_mod_xm_s3m.h" namespace Modules { class ModXmS3mStream : public Audio::AudioStream { private: struct Channel { Instrument *instrument; Sample *sample; Note note; int keyOn, randomSeed, plRow; int sampleOff, sampleIdx, sampleFra, freq, ampl, pann; int volume, panning, fadeoutVol, volEnvTick, panEnvTick; int period, portaPeriod, retrigCount, fxCount, avCount; int portaUpParam, portaDownParam, tonePortaParam, offsetParam; int finePortaUpParam, finePortaDownParam, xfinePortaParam; int arpeggioParam, volSlideParam, gvolSlideParam, panSlideParam; int fineVslideUpParam, fineVslideDownParam; int retrigVolume, retrigTicks, tremorOnTicks, tremorOffTicks; int vibratoType, vibratoPhase, vibratoSpeed, vibratoDepth; int tremoloType, tremoloPhase, tremoloSpeed, tremoloDepth; int tremoloAdd, vibratoAdd, arpeggioAdd; }; ModuleModXmS3m _module; bool _loadSuccess; int _sampleRate, _interpolation, _globalVol; int _seqPos, _breakPos, _row, _nextRow, _tick; int _speed, _tempo, _plCount, _plChan; int *_rampBuf; int8 **_playCount; Channel *_channels; int _dataLeft; // mix buffer to keep a partially consumed decoded tick. int *_mixBuffer; int _mixBufferSamples; // number of samples kept in _mixBuffer static const int FP_SHIFT; static const int FP_ONE; static const int FP_MASK; static const int16 sinetable[]; int calculateDuration(); int calculateTickLength() { return (_sampleRate * 5) / (_tempo * 2); } int calculateMixBufLength() { return (calculateTickLength() + 65) * 4; } int initPlayCount(int8 **playCount); void setSequencePos(int pos); int tick(); void updateRow(); int seek(int samplePos); // Sample void downsample(int *buf, int count); void resample(Channel &channel, int *mixBuf, int offset, int count, int sampleRate); void updateSampleIdx(Channel &channel, int count, int sampleRate); // Channel void initChannel(int idx); void tickChannel(Channel &channel); void updateChannelRow(Channel &channel, Note note); // Effects int waveform(Channel &channel, int phase, int type); void vibrato(Channel &channel, int fine); void autoVibrato(Channel &channel); void portaUp(Channel &channel, int param); void portaDown(Channel &channel, int param); void tonePorta(Channel &channel); void volumeSlide(Channel &channel); void retrigVolSlide(Channel &channel); void tremolo(Channel &channel); void tremor(Channel &channel); void trigger(Channel &channel); void calculateFreq(Channel &channel); void calculateAmpl(Channel &channel); // Envelopes void updateEnvelopes(Channel &channel); int envelopeNextTick(Envelope &envelope, int tick, int keyOn); int calculateAmpl(Envelope &envelope, int tick); // Read stream int getAudio(int *mixBuf); void volumeRamp(int *mixBuf, int tickLen); public: // Check if module loading succeeds bool loadSuccess() const { return _loadSuccess; } // Implement virtual functions virtual int readBuffer(int16 *buffer, const int numSamples); virtual bool isStereo() const { return true; } virtual int getRate() const { return _sampleRate; } virtual bool endOfData() const { return _dataLeft <= 0; } ModXmS3mStream(Common::SeekableReadStream *stream, int rate, int interpolation); ~ModXmS3mStream(); }; const int ModXmS3mStream::FP_SHIFT = 0xF; const int ModXmS3mStream::FP_ONE = 0x8000; const int ModXmS3mStream::FP_MASK = 0x7FFF; const short ModXmS3mStream::sinetable[] = { 0, 24, 49, 74, 97, 120, 141, 161, 180, 197, 212, 224, 235, 244, 250, 253, 255, 253, 250, 244, 235, 224, 212, 197, 180, 161, 141, 120, 97, 74, 49, 24 }; ModXmS3mStream::ModXmS3mStream(Common::SeekableReadStream *stream, int rate, int interpolation) { _rampBuf = nullptr; _playCount = nullptr; _channels = nullptr; if (!_module.load(*stream)) { warning("It's not a valid Mod/S3m/Xm sound file"); _loadSuccess = false; return; } // assign values _loadSuccess = true; _mixBufferSamples = 0; _sampleRate = rate; _interpolation = interpolation; _rampBuf = new int[128]; _channels = new Channel[_module.numChannels]; _dataLeft = calculateDuration() * 4; // stereo and uint16 _mixBuffer = nullptr; } ModXmS3mStream::~ModXmS3mStream() { if (_rampBuf) { delete[] _rampBuf; _rampBuf = nullptr; } if (_playCount) { delete[] _playCount; _playCount = nullptr; } if (_channels) { delete[] _channels; _channels = nullptr; } if (_mixBuffer) { delete []_mixBuffer; _mixBuffer = nullptr; } } int ModXmS3mStream::initPlayCount(int8 **playCount) { int len = 0; for (int idx = 0; idx < _module.sequenceLen; ++idx) { int pat = _module.sequence[idx]; int rows = (pat < _module.numPatterns) ? _module.patterns[pat].numRows : 0; if (playCount) { playCount[idx] = playCount[0] ? &playCount[0][len] : nullptr; } len += rows; } return len; } void ModXmS3mStream::initChannel(int idx) { memset(&_channels[idx], 0, sizeof(Channel)); _channels[idx].panning = _module.defaultPanning[idx]; _channels[idx].instrument = &_module.instruments[0]; _channels[idx].sample = &_module.instruments[0].samples[0]; _channels[idx].randomSeed = (idx + 1) * 0xABCDEF; } void ModXmS3mStream::tickChannel(Channel &channel) { channel.vibratoAdd = 0; channel.fxCount++; channel.retrigCount++; if (!(channel.note.effect == 0x7D && channel.fxCount <= channel.note.param)) { switch (channel.note.volume & 0xF0) { case 0x60: /* Vol Slide Down.*/ channel.volume -= channel.note.volume & 0xF; if (channel.volume < 0) { channel.volume = 0; } break; case 0x70: /* Vol Slide Up.*/ channel.volume += channel.note.volume & 0xF; if (channel.volume > 64) { channel.volume = 64; } break; case 0xB0: /* Vibrato.*/ channel.vibratoPhase += channel.vibratoSpeed; vibrato(channel, 0); break; case 0xD0: /* Pan Slide Left.*/ channel.panning -= channel.note.volume & 0xF; if (channel.panning < 0) { channel.panning = 0; } break; case 0xE0: /* Pan Slide Right.*/ channel.panning += channel.note.volume & 0xF; if (channel.panning > 255) { channel.panning = 255; } break; case 0xF0: /* Tone Porta.*/ tonePorta(channel); break; } } switch (channel.note.effect) { case 0x01: case 0x86: /* Porta Up. */ portaUp(channel, channel.portaUpParam); break; case 0x02: case 0x85: /* Porta Down. */ portaDown(channel, channel.portaDownParam); break; case 0x03: case 0x87: /* Tone Porta. */ tonePorta(channel); break; case 0x04: case 0x88: /* Vibrato. */ channel.vibratoPhase += channel.vibratoSpeed; vibrato(channel, 0); break; case 0x05: case 0x8C: /* Tone Porta + Vol Slide. */ tonePorta(channel); volumeSlide(channel); break; case 0x06: case 0x8B: /* Vibrato + Vol Slide. */ channel.vibratoPhase += channel.vibratoSpeed; vibrato(channel, 0); volumeSlide(channel); break; case 0x07: case 0x92: /* Tremolo. */ channel.tremoloPhase += channel.tremoloSpeed; tremolo(channel); break; case 0x0A: case 0x84: /* Vol Slide. */ volumeSlide(channel); break; case 0x11: /* Global Volume Slide. */ _globalVol = _globalVol + (channel.gvolSlideParam >> 4) - (channel.gvolSlideParam & 0xF); if (_globalVol < 0) { _globalVol = 0; } if (_globalVol > 64) { _globalVol = 64; } break; case 0x19: /* Panning Slide. */ channel.panning = channel.panning + (channel.panSlideParam >> 4) - (channel.panSlideParam & 0xF); if (channel.panning < 0) { channel.panning = 0; } if (channel.panning > 255) { channel.panning = 255; } break; case 0x1B: case 0x91: /* Retrig + Vol Slide. */ retrigVolSlide(channel); break; case 0x1D: case 0x89: /* Tremor. */ tremor(channel); break; case 0x79: /* Retrig. */ if (channel.fxCount >= channel.note.param) { channel.fxCount = 0; channel.sampleIdx = channel.sampleFra = 0; } break; case 0x7C: case 0xFC: /* Note Cut. */ if (channel.note.param == channel.fxCount) { channel.volume = 0; } break; case 0x7D: case 0xFD: /* Note Delay. */ if (channel.note.param == channel.fxCount) { trigger(channel); } break; case 0x8A: /* Arpeggio. */ if (channel.fxCount == 1) { channel.arpeggioAdd = channel.arpeggioParam >> 4; } else if (channel.fxCount == 2) { channel.arpeggioAdd = channel.arpeggioParam & 0xF; } else { channel.arpeggioAdd = channel.fxCount = 0; } break; case 0x95: /* Fine Vibrato. */ channel.vibratoPhase += channel.vibratoSpeed; vibrato(channel, 1); break; } autoVibrato(channel); calculateFreq(channel); calculateAmpl(channel); updateEnvelopes(channel); } void ModXmS3mStream::volumeSlide(Channel &channel) { int up = channel.volSlideParam >> 4; int down = channel.volSlideParam & 0xF; if (down == 0xF && up > 0) { /* Fine slide up.*/ if (channel.fxCount == 0) { channel.volume += up; } } else if (up == 0xF && down > 0) { /* Fine slide down.*/ if (channel.fxCount == 0) { channel.volume -= down; } } else if (channel.fxCount > 0 || _module.fastVolSlides) { /* Normal.*/ channel.volume += up - down; } if (channel.volume > 64) { channel.volume = 64; } if (channel.volume < 0) { channel.volume = 0; } } void ModXmS3mStream::portaUp(Channel &channel, int param) { switch (param & 0xF0) { case 0xE0: /* Extra-fine porta.*/ if (channel.fxCount == 0) { channel.period -= param & 0xF; } break; case 0xF0: /* Fine porta.*/ if (channel.fxCount == 0) { channel.period -= (param & 0xF) << 2; } break; default:/* Normal porta.*/ if (channel.fxCount > 0) { channel.period -= param << 2; } break; } if (channel.period < 0) { channel.period = 0; } } void ModXmS3mStream::portaDown(Channel &channel, int param) { if (channel.period > 0) { switch (param & 0xF0) { case 0xE0: /* Extra-fine porta.*/ if (channel.fxCount == 0) { channel.period += param & 0xF; } break; case 0xF0: /* Fine porta.*/ if (channel.fxCount == 0) { channel.period += (param & 0xF) << 2; } break; default:/* Normal porta.*/ if (channel.fxCount > 0) { channel.period += param << 2; } break; } if (channel.period > 65535) { channel.period = 65535; } } } void ModXmS3mStream::tonePorta(Channel &channel) { if (channel.period > 0) { if (channel.period < channel.portaPeriod) { channel.period += channel.tonePortaParam << 2; if (channel.period > channel.portaPeriod) { channel.period = channel.portaPeriod; } } else { channel.period -= channel.tonePortaParam << 2; if (channel.period < channel.portaPeriod) { channel.period = channel.portaPeriod; } } } } int ModXmS3mStream::waveform(Channel &channel, int phase, int type) { int amplitude = 0; switch (type) { default: /* Sine. */ amplitude = sinetable[phase & 0x1F]; if ((phase & 0x20) > 0) { amplitude = -amplitude; } break; case 6: /* Saw Up.*/ amplitude = (((phase + 0x20) & 0x3F) << 3) - 255; break; case 1: case 7: /* Saw Down. */ amplitude = 255 - (((phase + 0x20) & 0x3F) << 3); break; case 2: case 5: /* Square. */ amplitude = (phase & 0x20) > 0 ? 255 : -255; break; case 3: case 8: /* Random. */ amplitude = (channel.randomSeed >> 20) - 255; channel.randomSeed = (channel.randomSeed * 65 + 17) & 0x1FFFFFFF; break; } return amplitude; } void ModXmS3mStream::vibrato(Channel &channel, int fine) { int wave = waveform(channel, channel.vibratoPhase, channel.vibratoType & 0x3); channel.vibratoAdd = wave * channel.vibratoDepth >> (fine ? 7 : 5); } void ModXmS3mStream::tremolo(Channel &channel) { int wave = waveform(channel, channel.tremoloPhase, channel.tremoloType & 0x3); channel.tremoloAdd = wave * channel.tremoloDepth >> 6; } void ModXmS3mStream::tremor(Channel &channel) { if (channel.retrigCount >= channel.tremorOnTicks) { channel.tremoloAdd = -64; } if (channel.retrigCount >= (channel.tremorOnTicks + channel.tremorOffTicks)) { channel.tremoloAdd = channel.retrigCount = 0; } } void ModXmS3mStream::retrigVolSlide(Channel &channel) { if (channel.retrigCount >= channel.retrigTicks) { channel.retrigCount = channel.sampleIdx = channel.sampleFra = 0; switch (channel.retrigVolume) { case 0x1: channel.volume = channel.volume - 1; break; case 0x2: channel.volume = channel.volume - 2; break; case 0x3: channel.volume = channel.volume - 4; break; case 0x4: channel.volume = channel.volume - 8; break; case 0x5: channel.volume = channel.volume - 16; break; case 0x6: channel.volume = channel.volume * 2 / 3; break; case 0x7: channel.volume = channel.volume >> 1; break; case 0x8: /* ? */ break; case 0x9: channel.volume = channel.volume + 1; break; case 0xA: channel.volume = channel.volume + 2; break; case 0xB: channel.volume = channel.volume + 4; break; case 0xC: channel.volume = channel.volume + 8; break; case 0xD: channel.volume = channel.volume + 16; break; case 0xE: channel.volume = channel.volume * 3 / 2; break; case 0xF: channel.volume = channel.volume << 1; break; } if (channel.volume < 0) { channel.volume = 0; } if (channel.volume > 64) { channel.volume = 64; } } } void ModXmS3mStream::trigger(Channel &channel) { int ins = channel.note.instrument; if (ins > 0 && ins <= _module.numInstruments) { channel.instrument = &_module.instruments[ins]; int key = channel.note.key < 97 ? channel.note.key : 0; int sam = channel.instrument->keyToSample[key]; Sample *sample = &channel.instrument->samples[sam]; channel.volume = sample->volume >= 64 ? 64 : sample->volume & 0x3F; if (sample->panning > 0) { channel.panning = (sample->panning - 1) & 0xFF; } if (channel.period > 0 && sample->loopLength > 1) { /* Amiga trigger.*/ channel.sample = sample; } channel.sampleOff = 0; channel.volEnvTick = channel.panEnvTick = 0; channel.fadeoutVol = 32768; channel.keyOn = 1; } if (channel.note.effect == 0x09 || channel.note.effect == 0x8F) { /* Set Sample Offset. */ if (channel.note.param > 0) { channel.offsetParam = channel.note.param; } channel.sampleOff = channel.offsetParam << 8; } if (channel.note.volume >= 0x10 && channel.note.volume < 0x60) { channel.volume = channel.note.volume < 0x50 ? channel.note.volume - 0x10 : 64; } switch (channel.note.volume & 0xF0) { case 0x80: /* Fine Vol Down.*/ channel.volume -= channel.note.volume & 0xF; if (channel.volume < 0) { channel.volume = 0; } break; case 0x90: /* Fine Vol Up.*/ channel.volume += channel.note.volume & 0xF; if (channel.volume > 64) { channel.volume = 64; } break; case 0xA0: /* Set Vibrato Speed.*/ if ((channel.note.volume & 0xF) > 0) { channel.vibratoSpeed = channel.note.volume & 0xF; } break; case 0xB0: /* Vibrato.*/ if ((channel.note.volume & 0xF) > 0) { channel.vibratoDepth = channel.note.volume & 0xF; } vibrato(channel, 0); break; case 0xC0: /* Set Panning.*/ channel.panning = (channel.note.volume & 0xF) * 17; break; case 0xF0: /* Tone Porta.*/ if ((channel.note.volume & 0xF) > 0) { channel.tonePortaParam = channel.note.volume & 0xF; } break; } if (channel.note.key > 0) { if (channel.note.key > 96) { channel.keyOn = 0; } else { int porta = (channel.note.volume & 0xF0) == 0xF0 || channel.note.effect == 0x03 || channel.note.effect == 0x05 || channel.note.effect == 0x87 || channel.note.effect == 0x8C; if (!porta) { ins = channel.instrument->keyToSample[channel.note.key]; channel.sample = &channel.instrument->samples[ins]; } int finetune = channel.sample->finetune; if (channel.note.effect == 0x75 || channel.note.effect == 0xF2) { /* Set Fine Tune. */ finetune = ((channel.note.param & 0xF) << 4) - 128; } int key = channel.note.key + channel.sample->relNote; if (key < 1) { key = 1; } if (key > 120) { key = 120; } int period = (key << 6) + (finetune >> 1); if (_module.linearPeriods) { channel.portaPeriod = 7744 - period; } else { channel.portaPeriod = 29021 * ModuleModXmS3m::moduleExp2((period << FP_SHIFT) / -768) >> FP_SHIFT; } if (!porta) { channel.period = channel.portaPeriod; channel.sampleIdx = channel.sampleOff; channel.sampleFra = 0; if (channel.vibratoType < 4) { channel.vibratoPhase = 0; } if (channel.tremoloType < 4) { channel.tremoloPhase = 0; } channel.retrigCount = channel.avCount = 0; } } } } void ModXmS3mStream::updateEnvelopes(Channel &channel) { if (channel.instrument->volEnv.enabled) { if (!channel.keyOn) { channel.fadeoutVol -= channel.instrument->volFadeout; if (channel.fadeoutVol < 0) { channel.fadeoutVol = 0; } } channel.volEnvTick = envelopeNextTick(channel.instrument->volEnv, channel.volEnvTick, channel.keyOn); } if (channel.instrument->panEnv.enabled) { channel.panEnvTick = envelopeNextTick(channel.instrument->panEnv, channel.panEnvTick, channel.keyOn); } } void ModXmS3mStream::autoVibrato(Channel &channel) { int depth = channel.instrument->vibDepth & 0x7F; if (depth > 0) { int sweep = channel.instrument->vibSweep & 0x7F; int rate = channel.instrument->vibRate & 0x7F; int type = channel.instrument->vibType; if (channel.avCount < sweep) { depth = depth * channel.avCount / sweep; } int wave = waveform(channel, channel.avCount * rate >> 2, type + 4); channel.vibratoAdd += wave * depth >> 8; channel.avCount++; } } void ModXmS3mStream::calculateFreq(Channel &channel) { int per = channel.period + channel.vibratoAdd; if (_module.linearPeriods) { per = per - (channel.arpeggioAdd << 6); if (per < 28 || per > 7680) { per = 7680; } channel.freq = ((_module.c2Rate >> 4) * ModuleModXmS3m::moduleExp2(((4608 - per) << FP_SHIFT) / 768)) >> (FP_SHIFT - 4); } else { if (per > 29021) { per = 29021; } per = (per << FP_SHIFT) / ModuleModXmS3m::moduleExp2((channel.arpeggioAdd << FP_SHIFT) / 12); if (per < 28) { per = 29021; } channel.freq = _module.c2Rate * 1712 / per; } } void ModXmS3mStream::calculateAmpl(Channel &channel) { int envPan = 32, envVol = channel.keyOn ? 64 : 0; if (channel.instrument->volEnv.enabled) { envVol = calculateAmpl(channel.instrument->volEnv, channel.volEnvTick); } int vol = channel.volume + channel.tremoloAdd; if (vol > 64) { vol = 64; } if (vol < 0) { vol = 0; } vol = (vol * _module.gain * FP_ONE) >> 13; vol = (vol * channel.fadeoutVol) >> 15; channel.ampl = (vol * _globalVol * envVol) >> 12; if (channel.instrument->panEnv.enabled) { envPan = calculateAmpl(channel.instrument->panEnv, channel.panEnvTick); } int range = (channel.panning < 128) ? channel.panning : (255 - channel.panning); channel.pann = channel.panning + (range * (envPan - 32) >> 5); } void ModXmS3mStream::updateChannelRow(Channel &channel, Note note) { channel.note = note; channel.retrigCount++; channel.vibratoAdd = channel.tremoloAdd = channel.arpeggioAdd = channel.fxCount = 0; if (!((note.effect == 0x7D || note.effect == 0xFD) && note.param > 0)) { /* Not note delay.*/ trigger(channel); } switch (channel.note.effect) { case 0x01: case 0x86: /* Porta Up. */ if (channel.note.param > 0) { channel.portaUpParam = channel.note.param; } portaUp(channel, channel.portaUpParam); break; case 0x02: case 0x85: /* Porta Down. */ if (channel.note.param > 0) { channel.portaDownParam = channel.note.param; } portaDown(channel, channel.portaDownParam); break; case 0x03: case 0x87: /* Tone Porta. */ if (channel.note.param > 0) { channel.tonePortaParam = channel.note.param; } break; case 0x04: case 0x88: /* Vibrato. */ if ((channel.note.param >> 4) > 0) { channel.vibratoSpeed = channel.note.param >> 4; } if ((channel.note.param & 0xF) > 0) { channel.vibratoDepth = channel.note.param & 0xF; } vibrato(channel, 0); break; case 0x05: case 0x8C: /* Tone Porta + Vol Slide. */ if (channel.note.param > 0) { channel.volSlideParam = channel.note.param; } volumeSlide(channel); break; case 0x06: case 0x8B: /* Vibrato + Vol Slide. */ if (channel.note.param > 0) { channel.volSlideParam = channel.note.param; } vibrato(channel, 0); volumeSlide(channel); break; case 0x07: case 0x92: /* Tremolo. */ if ((channel.note.param >> 4) > 0) { channel.tremoloSpeed = channel.note.param >> 4; } if ((channel.note.param & 0xF) > 0) { channel.tremoloDepth = channel.note.param & 0xF; } tremolo(channel); break; case 0x08: /* Set Panning.*/ channel.panning = (channel.note.param < 128) ? (channel.note.param << 1) : 255; break; case 0x0A: case 0x84: /* Vol Slide. */ if (channel.note.param > 0) { channel.volSlideParam = channel.note.param; } volumeSlide(channel); break; case 0x0C: /* Set Volume. */ channel.volume = channel.note.param >= 64 ? 64 : channel.note.param & 0x3F; break; case 0x10: case 0x96: /* Set Global Volume. */ _globalVol = channel.note.param >= 64 ? 64 : channel.note.param & 0x3F; break; case 0x11: /* Global Volume Slide. */ if (channel.note.param > 0) { channel.gvolSlideParam = channel.note.param; } break; case 0x14: /* Key Off. */ channel.keyOn = 0; break; case 0x15: /* Set Envelope Tick. */ channel.volEnvTick = channel.panEnvTick = channel.note.param & 0xFF; break; case 0x19: /* Panning Slide. */ if (channel.note.param > 0) { channel.panSlideParam = channel.note.param; } break; case 0x1B: case 0x91: /* Retrig + Vol Slide. */ if ((channel.note.param >> 4) > 0) { channel.retrigVolume = channel.note.param >> 4; } if ((channel.note.param & 0xF) > 0) { channel.retrigTicks = channel.note.param & 0xF; } retrigVolSlide(channel); break; case 0x1D: case 0x89: /* Tremor. */ if ((channel.note.param >> 4) > 0) { channel.tremorOnTicks = channel.note.param >> 4; } if ((channel.note.param & 0xF) > 0) { channel.tremorOffTicks = channel.note.param & 0xF; } tremor(channel); break; case 0x21: /* Extra Fine Porta. */ if (channel.note.param > 0) { channel.xfinePortaParam = channel.note.param; } switch (channel.xfinePortaParam & 0xF0) { case 0x10: portaUp(channel, 0xE0 | (channel.xfinePortaParam & 0xF)); break; case 0x20: portaDown(channel, 0xE0 | (channel.xfinePortaParam & 0xF)); break; } break; case 0x71: /* Fine Porta Up. */ if (channel.note.param > 0) { channel.finePortaUpParam = channel.note.param; } portaUp(channel, 0xF0 | (channel.finePortaUpParam & 0xF)); break; case 0x72: /* Fine Porta Down. */ if (channel.note.param > 0) { channel.finePortaDownParam = channel.note.param; } portaDown(channel, 0xF0 | (channel.finePortaDownParam & 0xF)); break; case 0x74: case 0xF3: /* Set Vibrato Waveform. */ if (channel.note.param < 8) { channel.vibratoType = channel.note.param; } break; case 0x77: case 0xF4: /* Set Tremolo Waveform. */ if (channel.note.param < 8) { channel.tremoloType = channel.note.param; } break; case 0x7A: /* Fine Vol Slide Up. */ if (channel.note.param > 0) { channel.fineVslideUpParam = channel.note.param; } channel.volume += channel.fineVslideUpParam; if (channel.volume > 64) { channel.volume = 64; } break; case 0x7B: /* Fine Vol Slide Down. */ if (channel.note.param > 0) { channel.fineVslideDownParam = channel.note.param; } channel.volume -= channel.fineVslideDownParam; if (channel.volume < 0) { channel.volume = 0; } break; case 0x7C: case 0xFC: /* Note Cut. */ if (channel.note.param <= 0) { channel.volume = 0; } break; case 0x8A: /* Arpeggio. */ if (channel.note.param > 0) { channel.arpeggioParam = channel.note.param; } break; case 0x95: /* Fine Vibrato.*/ if ((channel.note.param >> 4) > 0) { channel.vibratoSpeed = channel.note.param >> 4; } if ((channel.note.param & 0xF) > 0) { channel.vibratoDepth = channel.note.param & 0xF; } vibrato(channel, 1); break; case 0xF8: /* Set Panning. */ channel.panning = channel.note.param * 17; break; } autoVibrato(channel); calculateFreq(channel); calculateAmpl(channel); updateEnvelopes(channel); } int ModXmS3mStream::tick() { int count = 1; if (--_tick <= 0) { _tick = _speed; updateRow(); } else { for (int idx = 0; idx < _module.numChannels; idx++) { tickChannel(_channels[idx]); } } if (_playCount && _playCount[0]) { count = _playCount[_seqPos][_row] - 1; } return count; } void ModXmS3mStream::updateRow() { if (_nextRow < 0) { _breakPos = _seqPos + 1; _nextRow = 0; } if (_breakPos >= 0) { if (_breakPos >= _module.sequenceLen) { _breakPos = _nextRow = 0; } while (_module.sequence[_breakPos] >= _module.numPatterns) { _breakPos++; if (_breakPos >= _module.sequenceLen) { _breakPos = _nextRow = 0; } } _seqPos = _breakPos; for (int idx = 0; idx < _module.numChannels; idx++) { _channels[idx].plRow = 0; } _breakPos = -1; } Pattern &pattern = _module.patterns[_module.sequence[_seqPos]]; _row = _nextRow; if (_row >= pattern.numRows) { _row = 0; } if (_playCount && _playCount[0]) { int count = _playCount[_seqPos][_row]; if (_plCount < 0 && count < 127) { _playCount[_seqPos][_row] = count + 1; } } _nextRow = _row + 1; if (_nextRow >= pattern.numRows) { _nextRow = -1; } for (int idx = 0; idx < _module.numChannels; ++idx) { Note note = pattern.getNote(_row, idx); if (note.effect == 0xE) { note.effect = 0x70 | (note.param >> 4); note.param &= 0xF; } if (note.effect == 0x93) { note.effect = 0xF0 | (note.param >> 4); note.param &= 0xF; } if (note.effect == 0 && note.param > 0) { note.effect = 0x8A; } Channel &channel = _channels[idx]; updateChannelRow(channel, note); switch (note.effect) { case 0x81: /* Set Speed. */ if (note.param > 0) { _tick = _speed = note.param; } break; case 0xB: case 0x82: /* Pattern Jump.*/ if (_plCount < 0) { _breakPos = note.param; _nextRow = 0; } break; case 0xD: case 0x83: /* Pattern Break.*/ if (_plCount < 0) { if (_breakPos < 0) { _breakPos = _seqPos + 1; } _nextRow = (note.param >> 4) * 10 + (note.param & 0xF); } break; case 0xF: /* Set Speed/Tempo.*/ if (note.param > 0) { if (note.param < 32) { _tick = _speed = note.param; } else { _tempo = note.param; } } break; case 0x94: /* Set Tempo.*/ if (note.param > 32) { _tempo = note.param; } break; case 0x76: case 0xFB: /* Pattern Loop.*/ if (note.param == 0) { /* Set loop marker on this channel. */ channel.plRow = _row; } if (channel.plRow < _row && _breakPos < 0) { /* Marker valid. */ if (_plCount < 0) { /* Not already looping, begin. */ _plCount = note.param; _plChan = idx; } if (_plChan == idx) { /* Next Loop.*/ if (_plCount == 0) { /* Loop finished. Invalidate current marker. */ channel.plRow = _row + 1; } else { /* Loop. */ _nextRow = channel.plRow; } _plCount--; } } break; case 0x7E: case 0xFE: /* Pattern Delay.*/ _tick = _speed + _speed * note.param; break; } } } int ModXmS3mStream::envelopeNextTick(Envelope &envelope, int tick, int keyOn) { tick++; if (envelope.looped && tick >= envelope.loopEndTick) { tick = envelope.loopStartTick; } if (envelope.sustain && keyOn && tick >= envelope.sustainTick) { tick = envelope.sustainTick; } return tick; } int ModXmS3mStream::calculateAmpl(Envelope &envelope, int tick) { int idx, point, dt, da; int ampl = envelope.pointsAmpl[envelope.numPoints - 1]; if (tick < envelope.pointsTick[envelope.numPoints - 1]) { point = 0; for (idx = 1; idx < envelope.numPoints; idx++) { if (envelope.pointsTick[idx] <= tick) { point = idx; } } dt = envelope.pointsTick[point + 1] - envelope.pointsTick[point]; da = envelope.pointsAmpl[point + 1] - envelope.pointsAmpl[point]; ampl = envelope.pointsAmpl[point]; ampl += ((da << 24) / dt) * (tick - envelope.pointsTick[point]) >> 24; } return ampl; } int ModXmS3mStream::calculateDuration() { int count = 0, duration = 0; setSequencePos(0); while (count < 1) { duration += calculateTickLength(); count = tick(); } setSequencePos(0); return duration; } /* Seek to approximately the specified sample position. The actual sample position reached is returned. */ int ModXmS3mStream::seek(int samplePos) { int currentPos = 0; setSequencePos(0); int tickLen = calculateTickLength(); while ((samplePos - currentPos) >= tickLen) { for (int idx = 0; idx < _module.numChannels; ++idx) { updateSampleIdx(_channels[idx], tickLen * 2, _sampleRate * 2); } currentPos += tickLen; tick(); tickLen = calculateTickLength(); } return currentPos; } void ModXmS3mStream::resample(Channel &channel, int *mixBuf, int offset, int count, int sampleRate) { Sample *sample = channel.sample; int lGain = 0, rGain = 0, samIdx = 0, samFra = 0, step = 0; int loopLen = 0, loopEnd = 0, outIdx = 0, outEnd = 0, y = 0, m = 0, c = 0; int16 *sampleData = channel.sample->data; if (channel.ampl > 0) { lGain = channel.ampl * (255 - channel.pann) >> 8; rGain = channel.ampl * channel.pann >> 8; samIdx = channel.sampleIdx; samFra = channel.sampleFra; step = (channel.freq << (FP_SHIFT - 3)) / (sampleRate >> 3); loopLen = sample->loopLength; loopEnd = sample->loopStart + loopLen; outIdx = offset * 2; outEnd = (offset + count) * 2; if (_interpolation) { while (outIdx < outEnd) { if (samIdx >= loopEnd) { if (loopLen > 1) { while (samIdx >= loopEnd) { samIdx -= loopLen; } } else { break; } } c = sampleData[samIdx]; m = sampleData[samIdx + 1] - c; y = ((m * samFra) >> FP_SHIFT) + c; mixBuf[outIdx++] += (y * lGain) >> FP_SHIFT; mixBuf[outIdx++] += (y * rGain) >> FP_SHIFT; samFra += step; samIdx += samFra >> FP_SHIFT; samFra &= FP_MASK; } } else { while (outIdx < outEnd) { if (samIdx >= loopEnd) { if (loopLen > 1) { while (samIdx >= loopEnd) { samIdx -= loopLen; } } else { break; } } if (samIdx < 0) samIdx = 0; y = sampleData[samIdx]; mixBuf[outIdx++] += (y * lGain) >> FP_SHIFT; mixBuf[outIdx++] += (y * rGain) >> FP_SHIFT; samFra += step; samIdx += samFra >> FP_SHIFT; samFra &= FP_MASK; } } } } void ModXmS3mStream::updateSampleIdx(Channel &channel, int count, int sampleRate) { Sample *sample = channel.sample; int step = (channel.freq << (FP_SHIFT - 3)) / (sampleRate >> 3); channel.sampleFra += step * count; channel.sampleIdx += channel.sampleFra >> FP_SHIFT; if (channel.sampleIdx > (int)sample->loopStart) { if (sample->loopLength > 1) { channel.sampleIdx = sample->loopStart + (channel.sampleIdx - sample->loopStart) % sample->loopLength; } else { channel.sampleIdx = sample->loopStart; } } channel.sampleFra &= FP_MASK; } void ModXmS3mStream::volumeRamp(int *mixBuf, int tickLen) { int rampRate = 256 * 2048 / _sampleRate; for (int idx = 0, a1 = 0; a1 < 256; idx += 2, a1 += rampRate) { int a2 = 256 - a1; mixBuf[idx] = (mixBuf[idx] * a1 + _rampBuf[idx] * a2) >> 8; mixBuf[idx + 1] = (mixBuf[idx + 1] * a1 + _rampBuf[idx + 1] * a2) >> 8; } memcpy(_rampBuf, &mixBuf[tickLen * 2], 128 * sizeof(int)); } /* 2:1 downsampling with simple but effective anti-aliasing. Buf must contain count * 2 + 1 stereo samples. */ void ModXmS3mStream::downsample(int *buf, int count) { int outLen = count * 2; for (int idx = 0, outIdx = 0; outIdx < outLen; idx += 4, outIdx += 2) { buf[outIdx] = (buf[idx] >> 2) + (buf[idx + 2] >> 1) + (buf[idx + 4] >> 2); buf[outIdx + 1] = (buf[idx + 1] >> 2) + (buf[idx + 3] >> 1) + (buf[idx + 5] >> 2); } } /* Generates audio and returns the number of stereo samples written into mixBuf. */ int ModXmS3mStream::getAudio(int *mixBuf) { if (_mixBuffer) { memcpy(mixBuf, _mixBuffer, _mixBufferSamples * sizeof(int)); delete []_mixBuffer; _mixBuffer = nullptr; return _mixBufferSamples; } int tickLen = calculateTickLength(); /* Clear output buffer. */ memset(mixBuf, 0, (tickLen + 65) * 4 * sizeof(int)); /* Resample. */ for (int idx = 0; idx < _module.numChannels; idx++) { Channel &channel = _channels[idx]; resample(channel, mixBuf, 0, (tickLen + 65) * 2, _sampleRate * 2); updateSampleIdx(channel, tickLen * 2, _sampleRate * 2); } downsample(mixBuf, tickLen + 64); volumeRamp(mixBuf, tickLen); tick(); return tickLen * 2; // stereo samples } int ModXmS3mStream::readBuffer(int16 *buffer, const int numSamples) { int samplesRead = 0; while (samplesRead < numSamples && _dataLeft >= 0) { int *mixBuf = new int[calculateMixBufLength()]; int samples = getAudio(mixBuf); if (samplesRead + samples > numSamples) { _mixBufferSamples = samplesRead + samples - numSamples; samples -= _mixBufferSamples; _mixBuffer = new int[_mixBufferSamples]; memcpy(_mixBuffer, mixBuf + samples, _mixBufferSamples * sizeof(int)); } for (int idx = 0; idx < samples; ++idx) { int ampl = mixBuf[idx]; if (ampl > 32767) { ampl = 32767; } if (ampl < -32768) { ampl = -32768; } *buffer++ = ampl; } samplesRead += samples; delete []mixBuf; // free } _dataLeft -= samplesRead * 2; return samplesRead; } void ModXmS3mStream::setSequencePos(int pos) { if (pos >= _module.sequenceLen) { pos = 0; } _breakPos = pos; _nextRow = 0; _tick = 1; _globalVol = _module.defaultGvol; _speed = _module.defaultSpeed > 0 ? _module.defaultSpeed : 6; _tempo = _module.defaultTempo > 0 ? _module.defaultTempo : 125; _plCount = _plChan = -1; // play count if (_playCount) { delete[] _playCount[0]; delete[] _playCount; } _playCount = new int8 *[_module.sequenceLen]; memset(_playCount, 0, _module.sequenceLen * sizeof(int8 *)); int len = initPlayCount(_playCount); _playCount[0] = new int8[len]; memset(_playCount[0], 0, len * sizeof(int8)); initPlayCount(_playCount); for (int idx = 0; idx < _module.numChannels; ++idx) { initChannel(idx); } memset(_rampBuf, 0, 128 * sizeof(int)); tick(); } } // End of namespace Modules namespace Audio { AudioStream *makeModXmS3mStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate, int interpolation) { Modules::ModXmS3mStream *soundStream = new Modules::ModXmS3mStream(stream, rate, interpolation); if (disposeAfterUse == DisposeAfterUse::YES) delete stream; if (!soundStream->loadSuccess()) { delete soundStream; return nullptr; } return (AudioStream *)soundStream; } } // End of namespace Audio