diff options
-rw-r--r-- | common/frac.h | 3 | ||||
-rw-r--r-- | sound/mods/paula.cpp | 39 | ||||
-rw-r--r-- | sound/mods/paula.h | 19 | ||||
-rw-r--r-- | sound/mods/protracker.cpp | 16 |
4 files changed, 40 insertions, 37 deletions
diff --git a/common/frac.h b/common/frac.h index 1c2c622a2c..b1e6c518d1 100644 --- a/common/frac.h +++ b/common/frac.h @@ -46,6 +46,9 @@ enum { */ typedef int32 frac_t; +inline frac_t doubleToFrac(double value) { return (frac_t)(value * FRAC_ONE); } +inline double fracToDouble(frac_t value) { return ((double)value) / FRAC_ONE; } + inline frac_t intToFrac(int16 value) { return value << FRAC_BITS; } inline int16 fracToInt(frac_t value) { return value >> FRAC_BITS; } diff --git a/sound/mods/paula.cpp b/sound/mods/paula.cpp index 9061891c35..b4f7018343 100644 --- a/sound/mods/paula.cpp +++ b/sound/mods/paula.cpp @@ -75,14 +75,9 @@ int Paula::readBuffer(int16 *buffer, const int numSamples) { template<bool stereo> -inline void mixBuffer(int16 *&buf, const int8 *data, double &offset, double rate, int end, byte volume, byte panning) { +inline void mixBuffer(int16 *&buf, const int8 *data, frac_t &offset, frac_t rate, int end, byte volume, byte panning) { for (int i = 0; i < end; i++) { - // FIXME: We should avoid using floating point arithmetic here, since - // FP calculations and int<->FP conversions are very expensive on many - // architectures. - // So consider replacing offset and rate with fixed point values... - - const int32 tmp = ((int32) data[(int)offset]) * volume; + const int32 tmp = ((int32) data[fracToInt(offset)]) * volume; if (stereo) { *buf++ += (tmp * (255 - panning)) >> 7; *buf++ += (tmp * (panning)) >> 7; @@ -110,50 +105,46 @@ int Paula::readBufferIntern(int16 *buffer, const int numSamples) { if (!_voice[voice].data || (_voice[voice].period <= 0)) continue; - double frequency = (7093789.2 / 2.0) / _voice[voice].period; - double rate = frequency / _rate; - double offset = _voice[voice].offset; + const double frequency = (7093789.2 / 2.0) / _voice[voice].period; + frac_t rate = doubleToFrac(frequency / _rate); + frac_t offset = _voice[voice].offset; + frac_t sLen = intToFrac(_voice[voice].length); - int sLen = _voice[voice].length; const int8 *data = _voice[voice].data; int16 *p = buffer; int end = 0; - _voice[voice].volume = MIN((byte) 0x40, _voice[voice].volume); // If looping has been enabled and we see that we will have to loop // to generate enough samples, then use the "loop" branch. if ((_voice[voice].lengthRepeat > 2) && - ((int)(offset + nSamples * rate) >= sLen)) { + (offset + nSamples * rate >= sLen)) { int neededSamples = nSamples; while (neededSamples > 0) { - end = MIN(neededSamples, (int)((sLen - offset) / rate)); - - if (end == 0) { + if (sLen - offset < rate) { // This means that "rate" is too high, bigger than the sample size. // So we scale it down according to the euclidean algorithm. - while (rate > (sLen - offset)) - rate -= (sLen - offset); - - end = MIN(neededSamples, (int)((sLen - offset) / rate)); + rate %= sLen - offset; } + end = MIN(neededSamples, (sLen - offset) / rate); mixBuffer<stereo>(p, data, offset, rate, end, _voice[voice].volume, _voice[voice].panning); _voice[voice].offset = offset; neededSamples -= end; // If we read beyond the sample end, loop back to the start. - if (ceil(_voice[voice].offset) >= sLen) { + // TODO: Shouldn't we wrap around here? + if (_voice[voice].offset + FRAC_ONE > sLen) { _voice[voice].data = data = _voice[voice].dataRepeat; - _voice[voice].length = sLen = _voice[voice].lengthRepeat; + _voice[voice].length = _voice[voice].lengthRepeat; _voice[voice].offset = offset = 0; + sLen = intToFrac(_voice[voice].length); } } } else { if (offset < sLen) { // Sample data left? - end = MIN(nSamples, (int)((sLen - offset) / rate)); - + end = MIN(nSamples, (sLen - offset) / rate); mixBuffer<stereo>(p, data, offset, rate, end, _voice[voice].volume, _voice[voice].panning); _voice[voice].offset = offset; } diff --git a/sound/mods/paula.h b/sound/mods/paula.h index 1e196daf40..e86c05b7f8 100644 --- a/sound/mods/paula.h +++ b/sound/mods/paula.h @@ -27,6 +27,7 @@ #define SOUND_MODS_PAULA_H #include "sound/audiostream.h" +#include "common/frac.h" #include "common/mutex.h" namespace Audio { @@ -65,7 +66,7 @@ protected: uint32 lengthRepeat; int16 period; byte volume; - double offset; // FIXME: Avoid floating point at all cost!!! + frac_t offset; byte panning; // For stereo mixing: 0 = far left, 255 = far right }; @@ -99,22 +100,30 @@ protected: _voice[channel].volume = volume; } - void setChannelData(uint8 channel, const int8 *data, const int8 *dataRepeat, uint32 length, uint32 lengthRepeat, double offset = 0.0) { + void setChannelData(uint8 channel, const int8 *data, const int8 *dataRepeat, uint32 length, uint32 lengthRepeat, int32 offset = 0) { assert(channel < NUM_VOICES); + + // For now, we only support 32k samples, as we use 16bit fixed point arithmetics. + // If this ever turns out to be a problem, we can still enhance this code. + assert(0 <= offset && offset < 32768); + assert(length < 32768); + assert(lengthRepeat < 32768); + Channel &ch = _voice[channel]; ch.data = data; ch.dataRepeat = dataRepeat; ch.length = length; ch.lengthRepeat = lengthRepeat; - ch.offset = offset; + ch.offset = intToFrac(offset); } - void setChannelOffset(byte channel, double offset) { + void setChannelOffset(byte channel, frac_t offset) { assert(channel < NUM_VOICES); + assert(0 <= offset); _voice[channel].offset = offset; } - double getChannelOffset(byte channel) { + frac_t getChannelOffset(byte channel) { assert(channel < NUM_VOICES); return _voice[channel].offset; } diff --git a/sound/mods/protracker.cpp b/sound/mods/protracker.cpp index 102c265ce9..46ee3aabf7 100644 --- a/sound/mods/protracker.cpp +++ b/sound/mods/protracker.cpp @@ -65,7 +65,7 @@ private: struct { byte sample; uint16 period; - double offset; + frac_t offset; byte vol; byte finetune; @@ -197,13 +197,13 @@ void ProtrackerStream::updateRow() { _track[track].period = _module.noteToPeriod(note.note, _track[track].finetune); else _track[track].period = note.period; - _track[track].offset = 0.0; + _track[track].offset = 0; } } - const int exy = note.effect & 0xff; - const int ex = (note.effect >> 4) & 0xf; - const int ey = note.effect & 0xf; + const byte exy = note.effect & 0xff; + const byte ex = (note.effect >> 4) & 0xf; + const byte ey = note.effect & 0xf; int vol; switch (effect) { @@ -243,7 +243,7 @@ void ProtrackerStream::updateRow() { break; case 0x9: // Set sample offset if (exy) { - _track[track].offset = exy * 256; + _track[track].offset = intToFrac(exy * 256); setChannelOffset(track, _track[track].offset); } break; @@ -384,12 +384,12 @@ void ProtrackerStream::updateEffects() { break; // Pattern loop case 0x9: // Retrigger note if (ey && (_tick % ey) == 0) - _track[track].offset = 0.0; + _track[track].offset = 0; break; case 0xD: // Delay sample if (_tick == _track[track].delaySampleTick) { _track[track].sample = _track[track].delaySample; - _track[track].offset = 0.0; + _track[track].offset = 0; if (_track[track].sample) _track[track].vol = _module.sample[_track[track].sample - 1].vol; } |