aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sound/mixer.cpp191
1 files changed, 154 insertions, 37 deletions
diff --git a/sound/mixer.cpp b/sound/mixer.cpp
index 9aaae1176c..e528a43b65 100644
--- a/sound/mixer.cpp
+++ b/sound/mixer.cpp
@@ -242,19 +242,95 @@ SoundMixer::Channel_RAW::Channel_RAW(SoundMixer *mixer, void *sound, uint32 size
_size = _size >> 1;
}
+
+/*
+ * Class that performs cubic interpolation on integer data.
+ * It is expected that the data is equidistant, i.e. all have the same
+ * horizontal distance. This is obviously the case for sampled audio.
+ */
+class CubicInterpolator {
+protected:
+ int x0, x1, x2, x3;
+ int a, b, c, d;
+
+public:
+ CubicInterpolator(int a, int b, int c) : x0(2*a-b), x1(a), x2(b), x3(c)
+ {
+ // We use a simple linear interpolation for x0
+ updateCoefficients();
+ }
+
+ inline void feedData()
+ {
+ x0 = x1;
+ x1 = x2;
+ x2 = x3;
+ x3 = 2*x2-x1; // Simple linear interpolation
+ updateCoefficients();
+ }
+
+ inline void feedData(int xNew)
+ {
+ x0 = x1;
+ x1 = x2;
+ x2 = x3;
+ x3 = xNew;
+ updateCoefficients();
+ }
+
+ /* t must be a 16.16 fixed point number between 0 and 1 */
+ inline int interpolate(uint32 fp_pos)
+ {
+ int result = 0;
+ int t = fp_pos >> 8;
+ result = (a*t + b) >> 8;
+ result = (result * t + c) >> 8;
+ result = (result * t + d) >> 8;
+ result = (result/3 + 1) >> 1;
+
+ return result;
+ }
+
+protected:
+ inline void updateCoefficients()
+ {
+ a = ((-x0*2)+(x1*5)-(x2*4)+x3);
+ b = ((x0+x2-(2*x1))*6) << 8;
+ c = ((-4*x0)+x1+(x2*4)-x3) << 8;
+ d = (x1*6) << 8;
+ }
+};
+
static int16 *mix_signed_mono_8(int16 *data, uint * len_ptr, byte **s_ptr, uint32 *fp_pos_ptr,
int fp_speed, const int16 *vol_tab, byte *s_end)
{
uint32 fp_pos = *fp_pos_ptr;
byte *s = *s_ptr;
uint len = *len_ptr;
+
+ int inc = 1, result;
+ CubicInterpolator interp(vol_tab[*s], vol_tab[*(s+1)], vol_tab[*(s+2)]);
+
do {
- fp_pos += fp_speed;
- *data++ += vol_tab[*s];
- *data++ += vol_tab[*s];
- s += fp_pos >> 16;
- fp_pos &= 0x0000FFFF;
- } while ((--len) && (s < s_end));
+ do {
+ result = interp.interpolate(fp_pos);
+
+ *data++ += result;
+ *data++ += result;
+
+ fp_pos += fp_speed;
+ inc = fp_pos >> 16;
+ s += inc;
+ len--;
+ fp_pos &= 0x0000FFFF;
+ } while (!inc && len && (s < s_end));
+
+ if (s+2 < s_end)
+ interp.feedData(vol_tab[*(s+2)]);
+ else
+ interp.feedData();
+
+ } while (len && (s < s_end));
*fp_pos_ptr = fp_pos;
*s_ptr = s;
@@ -286,23 +362,13 @@ static int16 *mix_unsigned_mono_8(int16 *data, uint * len_ptr, byte **s_ptr, uin
uint32 fp_pos = *fp_pos_ptr;
byte *s = *s_ptr;
uint len = *len_ptr;
- int x0, x1, x2, x3;
- int a, b, c, d;
- int inc = 1, result, t;
- x0 = x1 = vol_tab[*s ^ 0x80];
- x2 = vol_tab[*(s+1) ^ 0x80];
- x3 = vol_tab[*(s+2) ^ 0x80];
+
+ int inc = 1, result;
+ CubicInterpolator interp(vol_tab[*s ^ 0x80], vol_tab[*(s+1) ^ 0x80], vol_tab[*(s+2) ^ 0x80]);
+
do {
- a = ((-x0*2)+(x1*5)-(x2*4)+x3);
- b = ((x0+x2-(2*x1))*6) << 8;
- c = ((-4*x0)+x1+(x2*4)-x3) << 8;
- d = (x1*6) << 8;
do {
- t = fp_pos >> 8;
- result = (a*t + b) >> 8;
- result = (result * t + c) >> 8;
- result = (result * t + d) >> 8;
- result = (result/3 + 1) >> 1;
+ result = interp.interpolate(fp_pos);
*data++ += result;
*data++ += result;
@@ -310,13 +376,15 @@ static int16 *mix_unsigned_mono_8(int16 *data, uint * len_ptr, byte **s_ptr, uin
fp_pos += fp_speed;
inc = fp_pos >> 16;
s += inc;
+ len--;
fp_pos &= 0x0000FFFF;
- } while ((--len) && !inc && (s < s_end));
- x0 = x1;
- x1 = x2;
- x2 = x3;
+ } while (!inc && len && (s < s_end));
+
if (s+2 < s_end)
- x3 = vol_tab[*(s+2) ^ 0x80];
+ interp.feedData(vol_tab[*(s+2) ^ 0x80]);
+ else
+ interp.feedData();
+
} while (len && (s < s_end));
*fp_pos_ptr = fp_pos;
@@ -336,6 +404,7 @@ static int16 *mix_signed_stereo_8(int16 *data, uint * len_ptr, byte **s_ptr, uin
static int16 *mix_unsigned_stereo_8(int16 *data, uint * len_ptr, byte **s_ptr, uint32 *fp_pos_ptr,
int fp_speed, const int16 *vol_tab, byte *s_end)
{
+#if OLD
uint32 fp_pos = *fp_pos_ptr;
byte *s = *s_ptr;
uint len = *len_ptr;
@@ -352,10 +421,48 @@ static int16 *mix_unsigned_stereo_8(int16 *data, uint * len_ptr, byte **s_ptr, u
*len_ptr = len;
return data;
+#else
+ uint32 fp_pos = *fp_pos_ptr;
+ byte *s = *s_ptr;
+ uint len = *len_ptr;
+
+ int inc = 1;
+ CubicInterpolator left(vol_tab[*s ^ 0x80], vol_tab[*(s+2) ^ 0x80], vol_tab[*(s+4) ^ 0x80]);
+ CubicInterpolator right(vol_tab[*(s+1) ^ 0x80], vol_tab[*(s+3) ^ 0x80], vol_tab[*(s+5) ^ 0x80]);
+
+ do {
+ do {
+ *data++ += left.interpolate(fp_pos);
+ *data++ += right.interpolate(fp_pos);
+
+ fp_pos += fp_speed;
+ inc = (fp_pos >> 16) << 1;
+ s += inc;
+ len--;
+ fp_pos &= 0x0000FFFF;
+ } while (!inc && len && (s < s_end));
+
+ if (s+5 < s_end) {
+ left.feedData(vol_tab[*(s+4) ^ 0x80]);
+ right.feedData(vol_tab[*(s+5) ^ 0x80]);
+ } else {
+ left.feedData();
+ right.feedData();
+ }
+
+ } while (len && (s < s_end));
+
+ *fp_pos_ptr = fp_pos;
+ *s_ptr = s;
+ *len_ptr = len;
+
+ return data;
+#endif
}
static int16 *mix_signed_mono_16(int16 *data, uint * len_ptr, byte **s_ptr, uint32 *fp_pos_ptr,
int fp_speed, const int16 *vol_tab, byte *s_end)
{
+ printf("mix_signed_mono_16\n");
uint32 fp_pos = *fp_pos_ptr;
unsigned char volume = ((int)vol_tab[1]) * 32 / 255;
byte *s = *s_ptr;
@@ -385,6 +492,7 @@ static int16 *mix_unsigned_mono_16(int16 *data, uint * len_ptr, byte **s_ptr, ui
static int16 *mix_signed_stereo_16(int16 *data, uint * len_ptr, byte **s_ptr, uint32 *fp_pos_ptr,
int fp_speed, const int16 *vol_tab, byte *s_end)
{
+ printf("mix_signed_stereo_16\n");
uint32 fp_pos = *fp_pos_ptr;
unsigned char volume = ((int)vol_tab[1]) * 32 / 255;
byte *s = *s_ptr;
@@ -604,9 +712,9 @@ SoundMixer::Channel_MP3::Channel_MP3(SoundMixer *mixer, void *sound, uint size,
.SO3 file, you may have to change this value.
When using Lame, it seems that the sound starts to have some volume about 50 ms
- from the start of the sound => we skip about 1024 samples.
+ from the start of the sound => we skip about 2 frames (at 22.05 khz).
*/
- _silence_cut = 1024;
+ _silence_cut = 576 * 2;
}
static inline int scale_sample(mad_fixed_t sample)
@@ -634,18 +742,27 @@ void SoundMixer::Channel_MP3::mix(int16 *data, uint len)
real_destroy();
return;
}
-
+
while (1) {
ch = _synth.pcm.samples[0] + _pos_in_frame;
+
+ /* Skip _silence_cut a the start */
+ if ((_pos_in_frame < _synth.pcm.length) && (_silence_cut > 0)) {
+ int diff = _synth.pcm.length - _pos_in_frame;
+
+ if (diff > _silence_cut)
+ diff = _silence_cut;
+ _silence_cut -= diff;
+ ch += diff;
+ _pos_in_frame += diff;
+ }
+
while ((_pos_in_frame < _synth.pcm.length) && (len > 0)) {
- if (_silence_cut > 0) {
- _silence_cut--;
- } else {
- int16 sample = (int16)((scale_sample(*ch++) * volume) / 32);
- *data++ += sample;
- *data++ += sample;
- len--;
- }
+ int16 sample = (int16)((scale_sample(*ch) * volume) / 32);
+ *data++ += sample;
+ *data++ += sample;
+ len--;
+ ch++;
_pos_in_frame++;
}
if (len == 0)