aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/frac.h3
-rw-r--r--sound/mods/paula.cpp39
-rw-r--r--sound/mods/paula.h19
-rw-r--r--sound/mods/protracker.cpp16
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;
}