From ba717bc5155c896b0376d23c374d4dc801794dac Mon Sep 17 00:00:00 2001 From: Travis Howell Date: Tue, 12 Feb 2008 03:26:05 +0000 Subject: Add patch #1891606 - Improved sound playback for Amiga SCUMM V2/V3 games. svn-id: r30849 --- engines/scumm/player_mod.cpp | 58 ++++++++++++++++++++++++++++++++++++++------ engines/scumm/player_mod.h | 4 ++- 2 files changed, 53 insertions(+), 9 deletions(-) (limited to 'engines/scumm') diff --git a/engines/scumm/player_mod.cpp b/engines/scumm/player_mod.cpp index 71842d74b0..e50e116c83 100644 --- a/engines/scumm/player_mod.cpp +++ b/engines/scumm/player_mod.cpp @@ -41,8 +41,9 @@ Player_MOD::Player_MOD(Audio::Mixer *mixer) { _channels[i].id = 0; _channels[i].vol = 0; _channels[i].freq = 0; - _channels[i].converter = NULL; _channels[i].input = NULL; + _channels[i].ctr = 0; + _channels[i].pos = 0; } _playproc = NULL; @@ -56,7 +57,6 @@ Player_MOD::~Player_MOD() { for (int i = 0; i < MOD_MAXCHANS; i++) { if (!_channels[i].id) continue; - delete _channels[i].converter; delete _channels[i].input; } } @@ -93,8 +93,10 @@ void Player_MOD::startChannel(int id, void *data, int size, int rate, uint8 vol, _channels[i].vol = vol; _channels[i].pan = pan; _channels[i].freq = rate; + _channels[i].ctr = 0; _channels[i].input = Audio::makeLinearInputStream((const byte*)data, size, rate, Audio::Mixer::FLAG_AUTOFREE | (loopStart != loopEnd ? Audio::Mixer::FLAG_LOOP : 0), loopStart, loopEnd); - _channels[i].converter = Audio::makeRateConverter(rate, _mixer->getOutputRate(), false, false); + // read the first sample + _channels[i].input->readBuffer(&_channels[i].pos, 1); } void Player_MOD::stopChannel(int id) { @@ -102,13 +104,13 @@ void Player_MOD::stopChannel(int id) { error("player_mod - attempted to stop channel id 0"); for (int i = 0; i < MOD_MAXCHANS; i++) { if (_channels[i].id == id) { - delete _channels[i].converter; - _channels[i].converter = NULL; delete _channels[i].input; _channels[i].input = NULL; _channels[i].id = 0; _channels[i].vol = 0; _channels[i].freq = 0; + _channels[i].ctr = 0; + _channels[i].pos = 0; } } } @@ -139,9 +141,9 @@ void Player_MOD::setChannelFreq(int id, int freq) { error("player_mod - attempted to set frequency for channel id 0"); for (int i = 0; i < MOD_MAXCHANS; i++) { if (_channels[i].id == id) { + if (freq > 31400) // this is about as high as WinUAE goes + freq = 31400; // can't easily verify on my own Amiga _channels[i].freq = freq; - delete _channels[i].converter; - _channels[i].converter = Audio::makeRateConverter(freq, _mixer->getOutputRate(), false, false); break; } } @@ -170,11 +172,51 @@ void Player_MOD::do_mix(int16 *data, uint len) { len = 0; } for (i = 0; i < MOD_MAXCHANS; i++) + { if (_channels[i].id) { Audio::st_volume_t vol_l = (127 - _channels[i].pan) * _channels[i].vol / 127; Audio::st_volume_t vol_r = (127 + _channels[i].pan) * _channels[i].vol / 127; - _channels[i].converter->flow(*_channels[i].input, &data[dpos*2], dlen, vol_l, vol_r); + for (uint j = 0; j < dlen; j++) + { + // simple linear resample, unbuffered + int delta = (uint32)(_channels[i].freq * 0x10000) / _samplerate; + uint16 cfrac = ~_channels[i].ctr & 0xFFFF; + if (_channels[i].ctr + delta < 0x10000) + cfrac = delta; + _channels[i].ctr += delta; + int32 cpos = _channels[i].pos * cfrac / 0x10000; + while (_channels[i].ctr >= 0x10000) + { + if (_channels[i].input->readBuffer(&_channels[i].pos, 1) != 1) + { // out of data + stopChannel(_channels[i].id); + goto skipchan; // exit 2 loops at once + } + _channels[i].ctr -= 0x10000; + if (_channels[i].ctr > 0x10000) + cpos += _channels[i].pos; + else cpos += (int32)(_channels[i].pos * (_channels[i].ctr & 0xFFFF)) / 0x10000; + } + int16 pos = 0; + // if too many samples play in a row, the calculation below will overflow and clip + // so try and split it up into pieces it can manage comfortably + while (cpos < -0x8000) + { + pos -= 0x80000000 / delta; + cpos += 0x8000; + } + while (cpos > 0x7FFF) + { + pos += 0x7FFF0000 / delta; + cpos -= 0x7FFF; + } + pos += cpos * 0x10000 / delta; + Audio::clampedAdd(data[(dpos + j) * 2 + 0], pos * vol_l / Audio::Mixer::kMaxMixerVolume); + Audio::clampedAdd(data[(dpos + j) * 2 + 1], pos * vol_r / Audio::Mixer::kMaxMixerVolume); + } } +skipchan: ; // channel ran out of data + } dpos += dlen; } } diff --git a/engines/scumm/player_mod.h b/engines/scumm/player_mod.h index 5756916ae0..ba709e2430 100644 --- a/engines/scumm/player_mod.h +++ b/engines/scumm/player_mod.h @@ -75,7 +75,9 @@ private: uint8 vol; int8 pan; uint16 freq; - Audio::RateConverter *converter; + + uint32 ctr; + int16 pos; Audio::AudioStream *input; }; -- cgit v1.2.3