mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-05 00:36:57 +00:00
1419 lines
39 KiB
C++
1419 lines
39 KiB
C++
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* 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::RewindableAudioStream {
|
|
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;
|
|
int _initialDataLength;
|
|
bool _finished;
|
|
|
|
// 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() const { return (_sampleRate * 5) / (_tempo * 2); }
|
|
int calculateMixBufLength() const { return (calculateTickLength() + 65) * 4; }
|
|
|
|
int initPlayCount(int8 **playCount);
|
|
void setSequencePos(int pos);
|
|
int tick();
|
|
void updateRow();
|
|
int seek(int samplePos);
|
|
bool rewind() override { setSequencePos(0); _dataLeft = _initialDataLength; return true; }
|
|
|
|
// Sample
|
|
void downsample(int *buf, int count);
|
|
void resample(const 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(const Envelope &envelope, int tick, int keyOn);
|
|
int calculateAmpl(const 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
|
|
int readBuffer(int16 *buffer, const int numSamples) override;
|
|
bool isStereo() const override { return true; }
|
|
int getRate() const override { return _sampleRate; }
|
|
bool endOfData() const override { return _dataLeft <= 0; }
|
|
|
|
ModXmS3mStream(Common::SeekableReadStream *stream, int initialPos, 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 initialPos, int rate, int interpolation) :
|
|
_rampBuf(nullptr), _playCount(nullptr), _channels(nullptr),
|
|
_mixBuffer(nullptr), _sampleRate(rate), _interpolation(interpolation),
|
|
_seqPos(initialPos), _mixBufferSamples(0), _finished(false) {
|
|
if (!_module.load(*stream)) {
|
|
warning("It's not a valid Mod/S3m/Xm sound file");
|
|
_loadSuccess = false;
|
|
return;
|
|
}
|
|
|
|
_loadSuccess = true;
|
|
_rampBuf = new int[128];
|
|
_channels = new Channel[_module.numChannels];
|
|
_initialDataLength = _dataLeft = calculateDuration() * 4; // stereo and uint16
|
|
}
|
|
|
|
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;
|
|
default:
|
|
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;
|
|
default:
|
|
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;
|
|
default:
|
|
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;
|
|
default:
|
|
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 & 0xFF;
|
|
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;
|
|
default:
|
|
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;
|
|
default:
|
|
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) {
|
|
_finished = false;
|
|
if (_breakPos >= _module.sequenceLen) {
|
|
// Hit the end.
|
|
_breakPos = _nextRow = 0;
|
|
_finished = true;
|
|
}
|
|
while (_module.sequence[_breakPos] >= _module.numPatterns) {
|
|
_breakPos++;
|
|
if (_breakPos >= _module.sequenceLen) {
|
|
// Hit the end.
|
|
_breakPos = _nextRow = 0;
|
|
_finished = true;
|
|
}
|
|
}
|
|
_seqPos = _breakPos;
|
|
for (int idx = 0; idx < _module.numChannels; idx++) {
|
|
_channels[idx].plRow = 0;
|
|
}
|
|
_breakPos = -1;
|
|
}
|
|
const 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;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int ModXmS3mStream::envelopeNextTick(const Envelope &envelope, int currentTick, int keyOn) {
|
|
int nextTick = currentTick + 1;
|
|
if (envelope.looped && nextTick >= envelope.loopEndTick) {
|
|
nextTick = envelope.loopStartTick;
|
|
}
|
|
if (envelope.sustain && keyOn && nextTick >= envelope.sustainTick) {
|
|
nextTick = envelope.sustainTick;
|
|
}
|
|
return nextTick;
|
|
}
|
|
|
|
int ModXmS3mStream::calculateAmpl(const Envelope &envelope, int currentTick) {
|
|
int idx, point, dt, da;
|
|
int ampl = envelope.pointsAmpl[envelope.numPoints - 1];
|
|
if (currentTick < envelope.pointsTick[envelope.numPoints - 1]) {
|
|
point = 0;
|
|
for (idx = 1; idx < envelope.numPoints; idx++) {
|
|
if (envelope.pointsTick[idx] <= currentTick) {
|
|
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) * (currentTick - envelope.pointsTick[point]) >> 24;
|
|
}
|
|
return ampl;
|
|
}
|
|
|
|
int ModXmS3mStream::calculateDuration() {
|
|
int count = 0, duration = 0;
|
|
setSequencePos(0);
|
|
// Count the length of sequences until we hit a bit we've played before (or the end).
|
|
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(const 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;
|
|
|
|
_dataLeft -= samples * 2;
|
|
|
|
delete []mixBuf; // free
|
|
}
|
|
|
|
if (_dataLeft <= 0 && !_finished) {
|
|
//
|
|
// The original data length calculation was used up but didn't hit
|
|
// the end, so this mod loops.
|
|
// From here can just reset data left to some sensible value, but
|
|
// it's actually infinite and we'll come back here.
|
|
//
|
|
_dataLeft = _initialDataLength;
|
|
}
|
|
|
|
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]();
|
|
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 {
|
|
|
|
RewindableAudioStream *makeModXmS3mStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int initialPos, int rate, int interpolation) {
|
|
Modules::ModXmS3mStream *soundStream = new Modules::ModXmS3mStream(stream, initialPos, rate, interpolation);
|
|
|
|
if (disposeAfterUse == DisposeAfterUse::YES)
|
|
delete stream;
|
|
|
|
if (!soundStream->loadSuccess()) {
|
|
delete soundStream;
|
|
return nullptr;
|
|
}
|
|
|
|
return soundStream;
|
|
}
|
|
|
|
bool probeModXmS3m(Common::SeekableReadStream *st) {
|
|
int32 setPos = st->pos();
|
|
|
|
// xm file
|
|
char sigXm[18] = { 0 };
|
|
st->read(sigXm, 17);
|
|
st->seek(setPos);
|
|
if (!memcmp(sigXm, "Extended Module: ", 17)) {
|
|
return true;
|
|
}
|
|
|
|
// s3m file
|
|
char sigS3m[4];
|
|
st->skip(44);
|
|
st->read(sigS3m, 4);
|
|
st->seek(setPos);
|
|
if (!memcmp(sigS3m, "SCRM", 4)) {
|
|
st->seek(setPos);
|
|
return true;
|
|
}
|
|
|
|
// amf file
|
|
char sigAmf[25] = {};
|
|
st->read(sigAmf, 24);
|
|
st->seek(setPos);
|
|
if (!memcmp(sigAmf, "ASYLUM Music Format V1.0", 24)) {
|
|
return true;
|
|
}
|
|
|
|
// mod file
|
|
char sigMod[4];
|
|
st->skip(1080);
|
|
st->read(sigMod, 4);
|
|
st->seek(setPos);
|
|
if (!memcmp(sigMod, "M.K.", 4) || !memcmp(sigMod, "M!K!", 4)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
} // End of namespace Audio
|