More Amiga V2/V3 sound updates from _Q_:

1. A Player_MOD class, basically acts as a simplified mixer that mixes at 60Hz intervals (or whatever interval you specify), this gives smooth music playback in player_v3a
2. Some changes to player_v3a as a result of #1, including reduced music volume
3. player_v2a, and the necessary additions to scummvm.cpp/scumm.h

svn-id: r10392
This commit is contained in:
Travis Howell 2003-09-24 06:56:30 +00:00
parent 09a01e4cbe
commit 4a62eb3e3b
13 changed files with 2794 additions and 1151 deletions

View file

@ -24,8 +24,6 @@
#include "base/engine.h"
#include "player_v3a.h"
#include "scumm.h"
#include "sound/mixer.h"
#include "common/timer.h"
static const uint16 note_freqs[4][12] = {
{0x06B0,0x0650,0x05F4,0x05A0,0x054C,0x0500,0x04B8,0x0474,0x0434,0x03F8,0x03C0,0x0388},
@ -44,13 +42,14 @@ Player_V3A::Player_V3A(Scumm *scumm) {
int i;
_scumm = scumm;
_system = scumm->_system;
_mixer = scumm->_mixer;
for (i = 0; i < V3A_MAXSFX; i++)
_sfx[i].id = _sfx[i].dur = 0;
for (i = 0; i < V3A_MAXMUS; i++)
_mus[i].id = _mus[i].dur = 0;
for (i = 0; i < V3A_MAXMUS; i++) {
_mus[i].id = 0;
_mus[i].dur = 0;
}
for (i = 0; i < V3A_MAXSFX; i++) {
_sfx[i].id = 0;
_sfx[i].dur = 0;
}
_curSong = 0;
_songData = NULL;
@ -59,36 +58,63 @@ Player_V3A::Player_V3A(Scumm *scumm) {
_music_timer = 0;
_maxvol = 255;
scumm->_timer->installProcedure(timerHandler, 16666, this);
_isinit = false;
_mod = new Player_MOD(scumm);
_mod->setUpdateProc(update_proc, this, 60);
}
Player_V3A::~Player_V3A() {
_scumm->_timer->releaseProcedure(timerHandler);
if (!_isinit)
return;
for (int i = 0; _wavetable[i] != NULL; i++) {
for (int j = 0; j < 6; j++) {
free(_wavetable[i]->_idat[j]);
free(_wavetable[i]->_ldat[j]);
int i;
delete _mod;
if (_isinit) {
for (i = 0; _wavetable[i] != NULL; i++) {
for (int j = 0; j < 6; j++) {
free(_wavetable[i]->_idat[j]);
free(_wavetable[i]->_ldat[j]);
}
free(_wavetable[i]);
}
free(_wavetable[i]);
free(_wavetable);
}
free(_wavetable);
}
void Player_V3A::setMasterVolume (int vol) {
_maxvol = vol;
_mod->setMasterVolume(vol);
}
int Player_V3A::getMusChan (int id) const {
int i;
for (i = 0; i < V3A_MAXMUS; i++) {
if (_mus[i].id == id)
break;
}
if (i == V3A_MAXMUS) {
if (id == 0)
warning("player_v3a - out of music channels");
return -1;
}
return i;
}
int Player_V3A::getSfxChan (int id) const {
int i;
for (i = 0; i < V3A_MAXSFX; i++) {
if (_sfx[i].id == id)
break;
}
if (i == V3A_MAXSFX) {
if (id == 0)
warning("player_v3a - out of sfx channels");
return -1;
}
return i;
}
void Player_V3A::stopAllSounds() {
int i;
for (i = 0; i < V3A_MAXMUS; i++) {
if (_mus[i].id)
_mixer->stopID(V3A_MUS_BASEID + i);
_mod->stopChannel(_mus[i].id);
_mus[i].id = 0;
_mus[i].dur = 0;
}
@ -98,7 +124,7 @@ void Player_V3A::stopAllSounds() {
_songData = NULL;
for (i = 0; i < V3A_MAXSFX; i++) {
if (_sfx[i].id)
_mixer->stopID(V3A_SFX_BASEID + i);
_mod->stopChannel(_sfx[i].id | 0x100);
_sfx[i].id = 0;
_sfx[i].dur = 0;
}
@ -106,10 +132,14 @@ void Player_V3A::stopAllSounds() {
void Player_V3A::stopSound(int nr) {
int i;
if (nr == 0) { // Amiga Loom does this near the end, when Chaos casts SILENCE on Hetchel
stopAllSounds();
return;
}
if (nr == _curSong) {
for (i = 0; i < V3A_MAXMUS; i++) {
if (_mus[i].id)
_mixer->stopID(V3A_MUS_BASEID + i);
_mod->stopChannel(_mus[i].id);
_mus[i].id = 0;
_mus[i].dur = 0;
}
@ -118,59 +148,21 @@ void Player_V3A::stopSound(int nr) {
_songDelay = 0;
_songData = NULL;
} else {
for (i = 0; i < V3A_MAXSFX; i++) {
if (_sfx[i].id == nr) {
_mixer->stopID(V3A_SFX_BASEID + i);
_sfx[i].id = 0;
_sfx[i].dur = 0;
break;
}
i = getSfxChan(nr);
if (i != -1) {
_mod->stopChannel(nr | 0x100);
_sfx[i].id = 0;
_sfx[i].dur = 0;
}
}
}
void Player_V3A::playSoundSFX (int nr, char *data, int size, int rate, int vol, int tl, bool looped, int loopStart, int loopEnd) {
int i;
for (i = 0; i < V3A_MAXSFX; i++) {
if (!_sfx[i].id)
break;
}
if (i == V3A_MAXSFX) {
warning("player_v3a - too many sound effects playing (%i max)",V3A_MAXSFX);
return;
}
_sfx[i].id = nr;
_sfx[i].dur = tl;
vol = (vol * _maxvol) / 255;
_mixer->playRaw(NULL, data, size, rate, SoundMixer::FLAG_AUTOFREE | (looped ? SoundMixer::FLAG_LOOP : 0),
V3A_SFX_BASEID + i, vol, 0, loopStart, loopEnd);
}
void Player_V3A::playSoundMUS (char *data, int size, int rate, int vol, int tl, bool looped, int loopStart, int loopEnd) {
int i;
for (i = 0; i < V3A_MAXMUS; i++) {
if (!_mus[i].id)
break;
}
if (i == V3A_MAXMUS) {
warning("player_v3a - too many music channels playing (%i max)",V3A_MAXMUS);
return;
}
_mus[i].id = i + 1;
_mus[i].dur = tl;
vol = (vol * _maxvol) / 255;
_mixer->playRaw(NULL, data, size, rate, SoundMixer::FLAG_AUTOFREE | (looped ? SoundMixer::FLAG_LOOP : 0),
V3A_MUS_BASEID + i, vol, 0, loopStart, loopEnd);
}
void Player_V3A::startSound(int nr) {
assert(_scumm);
byte *data = _scumm->getResourceAddress(rtSound, nr);
assert(data);
if (_scumm->_gameId != GID_INDY3 && _scumm->_gameId != GID_LOOM)
if ((_scumm->_gameId != GID_INDY3) && (_scumm->_gameId != GID_LOOM))
error("player_v3a - unknown game!");
if (!_isinit) {
@ -223,6 +215,8 @@ void Player_V3A::startSound(int nr) {
stopSound(nr); // if a sound is playing, restart it
if (data[26]) {
if (_curSong)
stopSound(_curSong);
_curSong = nr;
_songData = data;
_songPtr = 0x1C;
@ -232,43 +226,49 @@ void Player_V3A::startSound(int nr) {
int size = READ_BE_UINT16(data + 12);
int rate = 3579545 / READ_BE_UINT16(data + 20);
char *sound = (char *)malloc(size);
int vol = (data[24] << 2) | (data[24] >> 4);
int vol = (data[24] << 1) | (data[24] >> 5); // if I boost this to 0-255, it gets too loud and starts to clip
memcpy(sound,data + READ_BE_UINT16(data + 8),size);
int loopStart = 0, loopEnd = 0;
bool looped = false;
if ((READ_BE_UINT16(data + 16) || READ_BE_UINT16(data + 6))) {
// the first check is for complex (pitch-bending) looped sounds
// the second check is for simple looped sounds
int loopStart = READ_BE_UINT16(data + 10) - READ_BE_UINT16(data + 8);
int loopEnd = READ_BE_UINT16(data + 14);
int tl = -1;
if ((_scumm->_gameId == GID_INDY3) && (nr == 60))
tl = 240; // the "airplane dive" sound needs to end on its own - the game won't stop it
playSoundSFX(nr, sound, size, rate, vol, tl, true, loopStart, loopEnd);
} else {
int tl = 1 + 60 * size / rate;
playSoundSFX(nr, sound, size, rate, vol, tl, false);
loopStart = READ_BE_UINT16(data + 10) - READ_BE_UINT16(data + 8);
loopEnd = READ_BE_UINT16(data + 14);
looped = true;
}
int i = getSfxChan();
_sfx[i].id = nr;
_sfx[i].dur = looped ? -1 : (1 + 60 * size / rate);
if ((_scumm->_gameId == GID_INDY3) && (nr == 60))
_sfx[i].dur = 240;
_mod->startChannel(nr | 0x100, sound, size, rate, vol, loopStart, loopEnd);
}
}
void Player_V3A::timerHandler(void *refCon) {
Player_V3A *player = (Player_V3A *)refCon;
assert(player);
player->playMusic();
void Player_V3A::update_proc(void *param) {
((Player_V3A *)param)->playMusic();
}
void Player_V3A::playMusic() {
int i;
for (i = 0; i < V3A_MAXSFX; i++) {
if ((_sfx[i].dur) && (!--_sfx[i].dur))
stopSound(_sfx[i].id);
}
for (i = 0; i < V3A_MAXMUS; i++) {
if ((_mus[i].dur) && (!--_mus[i].dur)) {
_scumm->_mixer->stopID(V3A_MUS_BASEID + i);
if (_mus[i].id) {
_mus[i].dur--;
if (_mus[i].dur)
continue;
_mod->stopChannel(_mus[i].id);
_mus[i].id = 0;
_mus[i].dur = 0;
}
}
for (i = 0; i < V3A_MAXSFX; i++) {
if (_sfx[i].id) {
_sfx[i].dur--;
if (_sfx[i].dur)
continue;
_mod->stopChannel(_sfx[i].id | 0x100);
_sfx[i].id = 0;
}
}
_music_timer++;
if (!_curSong)
return;
@ -295,8 +295,7 @@ void Player_V3A::playMusic() {
}
inst &= 0xF;
pit = _songData[_songPtr++];
vol = _songData[_songPtr++] & 0x7F;
vol = (vol << 1) | (vol >> 7); // 7-bit volume (Amiga drops the bottom bit), convert to 8-bit
vol = _songData[_songPtr++] & 0x7F; // if I boost this to 0-255, it gets too loud and starts to clip
dur = _songData[_songPtr++];
if (pit == 0) {
_songDelay = dur;
@ -309,13 +308,18 @@ void Player_V3A::playMusic() {
oct = 0;
if (oct > 5)
oct = 5;
int rate = 3579545 / note_freqs[_wavetable[inst]->_oct[oct]][pit];
char *data = (char *)malloc(_wavetable[inst]->_ilen[oct] + _wavetable[inst]->_llen[oct]);
if (_wavetable[inst]->_idat[oct])
memcpy(data, _wavetable[inst]->_idat[oct], _wavetable[inst]->_ilen[oct]);
if (_wavetable[inst]->_ldat[oct])
memcpy(data + _wavetable[inst]->_ilen[oct], _wavetable[inst]->_ldat[oct], _wavetable[inst]->_llen[oct]);
playSoundMUS(data, _wavetable[inst]->_ilen[oct] + _wavetable[inst]->_llen[oct], 3579545 / note_freqs[_wavetable[inst]->_oct[oct]][pit], vol, dur,
(_wavetable[inst]->_ldat[oct] != NULL), _wavetable[inst]->_ilen[oct], _wavetable[inst]->_ilen[oct] + _wavetable[inst]->_llen[oct]);
i = getMusChan();
_mus[i].id = i + 1;
_mus[i].dur = dur;
_mod->startChannel(_mus[i].id, data, _wavetable[inst]->_ilen[oct] + _wavetable[inst]->_llen[oct], rate, vol,
_wavetable[inst]->_ilen[oct], _wavetable[inst]->_ilen[oct] + _wavetable[inst]->_llen[oct]);
}
}
@ -326,8 +330,7 @@ int Player_V3A::getMusicTimer() const {
int Player_V3A::getSoundStatus(int nr) const {
if (nr == _curSong)
return 1;
for (int i = 0; i < V3A_MAXSFX; i++)
if (_sfx[i].id == nr)
return 1;
if (getSfxChan(nr) != -1)
return 1;
return 0;
}