mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-31 07:53:36 +00:00
f7d50f7c68
svn-id: r10305
334 lines
9.2 KiB
C++
334 lines
9.2 KiB
C++
/* ScummVM - Scumm Interpreter
|
|
* Copyright (C) 2001 Ludvig Strigeus
|
|
* Copyright (C) 2001-2003 The ScummVM project
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* $Header$
|
|
*
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
#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},
|
|
{0x0358,0x0328,0x02FA,0x02D0,0x02A6,0x0280,0x025C,0x023A,0x021A,0x01FC,0x01E0,0x01C4},
|
|
{0x01AC,0x0194,0x017D,0x0168,0x0153,0x0140,0x012E,0x011D,0x010D,0x00FE,0x00F0,0x00E2},
|
|
{0x00D6,0x00CA,0x00BE,0x00B4,0x00A9,0x00A0,0x0097,0x008E,0x0086,0x007F,0x00F0,0x00E2}
|
|
};
|
|
|
|
////////////////////////////////////////
|
|
//
|
|
// V3 Amiga sound/music driver
|
|
//
|
|
////////////////////////////////////////
|
|
|
|
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;
|
|
|
|
_curSong = 0;
|
|
_songData = NULL;
|
|
_songPtr = 0;
|
|
_songDelay = 0;
|
|
|
|
_music_timer = 0;
|
|
|
|
_maxvol = 255;
|
|
|
|
scumm->_timer->installProcedure(timerHandler, 16666, this);
|
|
|
|
_isinit = false;
|
|
}
|
|
|
|
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]);
|
|
}
|
|
free(_wavetable[i]);
|
|
}
|
|
free(_wavetable);
|
|
}
|
|
|
|
void Player_V3A::setMasterVolume (int vol) {
|
|
_maxvol = vol;
|
|
}
|
|
|
|
void Player_V3A::stopAllSounds() {
|
|
int i;
|
|
for (i = 0; i < V3A_MAXMUS; i++) {
|
|
if (_mus[i].id)
|
|
_mixer->stopID(V3A_MUS_BASEID + i);
|
|
_mus[i].id = 0;
|
|
_mus[i].dur = 0;
|
|
}
|
|
_curSong = 0;
|
|
_songPtr = 0;
|
|
_songDelay = 0;
|
|
_songData = NULL;
|
|
for (i = 0; i < V3A_MAXSFX; i++) {
|
|
if (_sfx[i].id)
|
|
_mixer->stopID(V3A_SFX_BASEID + i);
|
|
_sfx[i].id = 0;
|
|
_sfx[i].dur = 0;
|
|
}
|
|
}
|
|
|
|
void Player_V3A::stopSound(int nr) {
|
|
int i;
|
|
if (nr == _curSong) {
|
|
for (i = 0; i < V3A_MAXMUS; i++) {
|
|
if (_mus[i].id)
|
|
_mixer->stopID(V3A_MUS_BASEID + i);
|
|
_mus[i].id = 0;
|
|
_mus[i].dur = 0;
|
|
}
|
|
_curSong = 0;
|
|
_songPtr = 0;
|
|
_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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
error("player_v3a - unknown game!");
|
|
|
|
if (!_isinit) {
|
|
int i;
|
|
unsigned char *ptr;
|
|
int offset = 4;
|
|
int numInstruments;
|
|
|
|
if (_scumm->_gameId == GID_INDY3) {
|
|
ptr = _scumm->getResourceAddress(rtSound, 83);
|
|
numInstruments = 12;
|
|
} else {
|
|
ptr = _scumm->getResourceAddress(rtSound, 79);
|
|
numInstruments = 9;
|
|
}
|
|
assert(ptr);
|
|
_wavetable = (instData **)malloc((numInstruments + 1) * sizeof(void *));
|
|
for (i = 0; i < numInstruments; i++) {
|
|
_wavetable[i] = (instData *)malloc(sizeof(instData));
|
|
for (int j = 0; j < 6; j++) {
|
|
int off, len;
|
|
off = READ_BE_UINT16(ptr + offset + 0);
|
|
_wavetable[i]->_ilen[j] = len = READ_BE_UINT16(ptr + offset + 2);
|
|
if (len) {
|
|
_wavetable[i]->_idat[j] = (char *)malloc(len);
|
|
memcpy(_wavetable[i]->_idat[j],ptr + off,len);
|
|
} else _wavetable[i]->_idat[j] = NULL;
|
|
off = READ_BE_UINT16(ptr + offset + 4);
|
|
_wavetable[i]->_llen[j] = len = READ_BE_UINT16(ptr + offset + 6);
|
|
if (len) {
|
|
_wavetable[i]->_ldat[j] = (char *)malloc(len);
|
|
memcpy(_wavetable[i]->_ldat[j],ptr + off,len);
|
|
} else _wavetable[i]->_ldat[j] = NULL;
|
|
_wavetable[i]->_oct[j] = READ_BE_UINT16(ptr + offset + 8);
|
|
offset += 10;
|
|
}
|
|
if (_scumm->_gameId == GID_INDY3) {
|
|
_wavetable[i]->_pitadjust = 0;
|
|
offset += 2;
|
|
} else {
|
|
_wavetable[i]->_pitadjust = READ_BE_UINT16(ptr + offset + 2);
|
|
offset += 4;
|
|
}
|
|
}
|
|
_wavetable[i] = NULL;
|
|
_isinit = true;
|
|
}
|
|
|
|
if (getSoundStatus(nr))
|
|
stopSound(nr); // if a sound is playing, restart it
|
|
|
|
if (data[26]) {
|
|
_curSong = nr;
|
|
_songData = data;
|
|
_songPtr = 0x1C;
|
|
_songDelay = 1;
|
|
_music_timer = 0;
|
|
} else {
|
|
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);
|
|
memcpy(sound,data + READ_BE_UINT16(data + 8),size);
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player_V3A::timerHandler(void *refCon) {
|
|
Player_V3A *player = (Player_V3A *)refCon;
|
|
assert(player);
|
|
player->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);
|
|
_mus[i].id = 0;
|
|
_mus[i].dur = 0;
|
|
}
|
|
}
|
|
_music_timer++;
|
|
if (!_curSong)
|
|
return;
|
|
if (_songDelay && --_songDelay)
|
|
return;
|
|
if (((_songData[_songPtr] & 0xF0) != 0x80) && (_songData[_songPtr] != 0xFB)) {
|
|
// at the end of the song, and it wasn't looped - kill it
|
|
_curSong = 0;
|
|
return;
|
|
}
|
|
while (1) {
|
|
int inst, pit, vol, dur, oct;
|
|
inst = _songData[_songPtr++];
|
|
if ((inst & 0xF0) != 0x80) {
|
|
// tune is at the end - figure out what's still playing
|
|
// and see how long we have to wait until we stop/restart
|
|
for (i = 0; i < V3A_MAXMUS; i++) {
|
|
if (_songDelay < _mus[i].dur)
|
|
_songDelay = _mus[i].dur;
|
|
}
|
|
if (inst == 0xFB) // it's a looped song, restart it afterwards
|
|
_songPtr = 0x1C;
|
|
break;
|
|
}
|
|
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
|
|
dur = _songData[_songPtr++];
|
|
if (pit == 0) {
|
|
_songDelay = dur;
|
|
break;
|
|
}
|
|
pit += _wavetable[inst]->_pitadjust;
|
|
oct = (pit / 12) - 2;
|
|
pit = pit % 12;
|
|
if (oct < 0)
|
|
oct = 0;
|
|
if (oct > 5)
|
|
oct = 5;
|
|
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]);
|
|
}
|
|
}
|
|
|
|
int Player_V3A::getMusicTimer() const {
|
|
return _music_timer / 30;
|
|
}
|
|
|
|
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;
|
|
return 0;
|
|
}
|