scummvm/engines/agos/res_snd.cpp
Max Horn 82e473bc3b BACKENDS: Partial merge of gsoc2010-opengl: Audio CD changes only
This commit contains the AudioCDManager changes from the gsoc2010-opengl
branch. The other changes in that branch are restricted to the backends
directory only (plus configure).

The Nintendo DS and Dreamcast ports still need to be ported over to
the new Audio CD system, but that should be fairly easy to do.

svn-id: r54147
2010-11-08 22:53:36 +00:00

621 lines
15 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/config-manager.h"
#include "common/file.h"
#include "agos/intern.h"
#include "agos/agos.h"
#include "agos/vga.h"
#include "sound/audiostream.h"
#include "sound/mididrv.h"
#include "sound/mods/protracker.h"
namespace AGOS {
void AGOSEngine_Simon1::playSpeech(uint16 speech_id, uint16 vgaSpriteId) {
if (speech_id == 9999) {
if (_subtitles)
return;
if (!getBitFlag(14) && !getBitFlag(28)) {
setBitFlag(14, true);
_variableArray[100] = 15;
animate(4, 1, 130, 0, 0, 0);
waitForSync(130);
}
_skipVgaWait = true;
} else {
if (_subtitles && _scriptVar2) {
animate(4, 2, 204, 0, 0, 0);
waitForSync(204);
stopAnimate(204);
}
if (vgaSpriteId < 100)
stopAnimate(201 + vgaSpriteId);
loadVoice(speech_id);
if (vgaSpriteId < 100)
animate(4, 2, 201 + vgaSpriteId, 0, 0, 0);
}
}
void AGOSEngine_Simon2::playSpeech(uint16 speech_id, uint16 vgaSpriteId) {
if (speech_id == 0xFFFF) {
if (_subtitles)
return;
if (!getBitFlag(14) && !getBitFlag(28)) {
setBitFlag(14, true);
_variableArray[100] = 5;
animate(4, 1, 30, 0, 0, 0);
waitForSync(130);
}
_skipVgaWait = true;
} else {
if (getGameType() == GType_SIMON2 && _subtitles && _language != Common::HE_ISR) {
loadVoice(speech_id);
return;
}
if (_subtitles && _scriptVar2) {
animate(4, 2, 5, 0, 0, 0);
waitForSync(205);
stopAnimateSimon2(2,5);
}
stopAnimateSimon2(2, vgaSpriteId + 2);
loadVoice(speech_id);
animate(4, 2, vgaSpriteId + 2, 0, 0, 0);
}
}
void AGOSEngine::skipSpeech() {
_sound->stopVoice();
if (!getBitFlag(28)) {
setBitFlag(14, true);
if (getGameType() == GType_FF) {
_variableArray[103] = 5;
animate(4, 2, 13, 0, 0, 0);
waitForSync(213);
stopAnimateSimon2(2, 1);
} else if (getGameType() == GType_SIMON2) {
_variableArray[100] = 5;
animate(4, 1, 30, 0, 0, 0);
waitForSync(130);
stopAnimateSimon2(2, 1);
} else {
_variableArray[100] = 15;
animate(4, 1, 130, 0, 0, 0);
waitForSync(130);
stopAnimate(1);
}
}
}
void AGOSEngine::loadMusic(uint16 music) {
char buf[4];
stopMusic();
_gameFile->seek(_gameOffsetsPtr[_musicIndexBase + music - 1], SEEK_SET);
_gameFile->read(buf, 4);
if (!memcmp(buf, "FORM", 4)) {
_gameFile->seek(_gameOffsetsPtr[_musicIndexBase + music - 1], SEEK_SET);
_midi.loadXMIDI(_gameFile);
} else {
_gameFile->seek(_gameOffsetsPtr[_musicIndexBase + music - 1], SEEK_SET);
_midi.loadMultipleSMF(_gameFile);
}
_lastMusicPlayed = music;
_nextMusicToPlay = -1;
}
struct ModuleOffs {
uint8 tune;
uint8 fileNum;
uint32 offs;
};
static const ModuleOffs amigaWaxworksOffs[20] = {
// Pyramid
{2, 2, 0, },
{3, 2, 50980},
{4, 2, 56160},
{5, 2, 62364},
{6, 2, 73688},
// Zombie
{8, 8, 0},
{11, 8, 51156},
{12, 8, 56336},
{13, 8, 65612},
{14, 8, 68744},
// Mine
{9, 9, 0},
{15, 9, 47244},
{16, 9, 52424},
{17, 9, 59652},
{18, 9, 62784},
// Jack
{10, 10, 0},
{19, 10, 42054},
{20, 10, 47234},
{21, 10, 49342},
{22, 10, 51450},
};
void AGOSEngine::playModule(uint16 music) {
char filename[15];
Common::File f;
uint32 offs = 0;
if (getPlatform() == Common::kPlatformAmiga && getGameType() == GType_WW) {
// Multiple tunes are stored in music files for main locations
for (uint i = 0; i < 20; i++) {
if (amigaWaxworksOffs[i].tune == music) {
music = amigaWaxworksOffs[i].fileNum;
offs = amigaWaxworksOffs[i].offs;
}
}
}
if (getGameType() == GType_ELVIRA1 && getFeatures() & GF_DEMO)
sprintf(filename, "elvira2");
else if (getPlatform() == Common::kPlatformAcorn)
sprintf(filename, "%dtune.DAT", music);
else
sprintf(filename, "%dtune", music);
f.open(filename);
if (f.isOpen() == false) {
error("playModule: Can't load module from '%s'", filename);
}
Audio::AudioStream *audioStream;
if (!(getGameType() == GType_ELVIRA1 && getFeatures() & GF_DEMO) &&
getFeatures() & GF_CRUNCHED) {
uint32 srcSize = f.size();
byte *srcBuf = (byte *)malloc(srcSize);
if (f.read(srcBuf, srcSize) != srcSize)
error("playModule: Read failed");
uint32 dstSize = READ_BE_UINT32(srcBuf + srcSize - 4);
byte *dstBuf = (byte *)malloc(dstSize);
decrunchFile(srcBuf, dstBuf, srcSize);
free(srcBuf);
Common::MemoryReadStream stream(dstBuf, dstSize);
audioStream = Audio::makeProtrackerStream(&stream, offs);
free(dstBuf);
} else {
audioStream = Audio::makeProtrackerStream(&f);
}
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_modHandle, audioStream);
}
void AGOSEngine_Simon1::playMusic(uint16 music, uint16 track) {
stopMusic();
// Support for compressed music from the ScummVM Music Enhancement Project
_system->getAudioCDManager()->stop();
_system->getAudioCDManager()->play(music + 1, -1, 0, 0);
if (_system->getAudioCDManager()->isPlaying())
return;
if (getPlatform() == Common::kPlatformAmiga) {
playModule(music);
} else if (getFeatures() & GF_TALKIE) {
char buf[4];
// WORKAROUND: For a script bug in the CD versions
// We skip this music resource, as it was replaced by
// a sound effect, and the script was never updated.
if (music == 35)
return;
_midi.setLoop(true); // Must do this BEFORE loading music. (GMF may have its own override.)
_gameFile->seek(_gameOffsetsPtr[_musicIndexBase + music], SEEK_SET);
_gameFile->read(buf, 4);
if (!memcmp(buf, "GMF\x1", 4)) {
_gameFile->seek(_gameOffsetsPtr[_musicIndexBase + music], SEEK_SET);
_midi.loadSMF(_gameFile, music);
} else {
_gameFile->seek(_gameOffsetsPtr[_musicIndexBase + music], SEEK_SET);
_midi.loadMultipleSMF(_gameFile);
}
_midi.startTrack(0);
_midi.startTrack(track);
} else if (getPlatform() == Common::kPlatformAcorn) {
// TODO: Add support for Desktop Tracker format in Acorn disk version
} else {
char filename[15];
Common::File f;
sprintf(filename, "MOD%d.MUS", music);
f.open(filename);
if (f.isOpen() == false)
error("playMusic: Can't load music from '%s'", filename);
_midi.setLoop(true); // Must do this BEFORE loading music. (GMF may have its own override.)
if (getFeatures() & GF_DEMO)
_midi.loadS1D(&f);
else
_midi.loadSMF(&f, music);
_midi.startTrack(0);
_midi.startTrack(track);
}
}
void AGOSEngine::playMusic(uint16 music, uint16 track) {
stopMusic();
if (getPlatform() == Common::kPlatformAmiga) {
playModule(music);
} else if (getPlatform() == Common::kPlatformAtariST) {
// TODO: Add support for music formats used
} else {
_midi.setLoop(true); // Must do this BEFORE loading music.
char filename[15];
Common::File f;
sprintf(filename, "MOD%d.MUS", music);
f.open(filename);
if (f.isOpen() == false)
error("playMusic: Can't load music from '%s'", filename);
_midi.loadS1D(&f);
_midi.startTrack(0);
_midi.startTrack(track);
}
}
void AGOSEngine::stopMusic() {
if (_midiEnabled) {
_midi.stop();
}
_mixer->stopHandle(_modHandle);
}
void AGOSEngine::playSting(uint16 soundId) {
if (!_midi._enable_sfx)
return;
char filename[15];
Common::File mus_file;
uint16 mus_offset;
sprintf(filename, "STINGS%i.MUS", _soundFileId);
mus_file.open(filename);
if (!mus_file.isOpen())
error("playSting: Can't load sound effect from '%s'", filename);
mus_file.seek(soundId * 2, SEEK_SET);
mus_offset = mus_file.readUint16LE();
if (mus_file.err())
error("playSting: Can't read sting %d offset", soundId);
mus_file.seek(mus_offset, SEEK_SET);
_midi.loadSMF(&mus_file, soundId, true);
_midi.startTrack(0);
}
static const byte elvira1_soundTable[100] = {
0, 2, 0, 1, 0, 0, 0, 0, 0, 3,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 6, 4, 0, 0, 9, 0,
0, 2, 0, 0, 0, 0, 0, 0, 0, 0,
0, 8, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 0, 0, 5, 0, 6, 6, 0, 0,
0, 5, 0, 0, 6, 0, 0, 0, 0, 8,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
bool AGOSEngine::loadVGASoundFile(uint16 id, uint8 type) {
Common::File in;
char filename[15];
byte *dst;
uint32 srcSize, dstSize;
if (getPlatform() == Common::kPlatformAmiga || getPlatform() == Common::kPlatformAtariST) {
if (getGameType() == GType_ELVIRA1 && (getFeatures() & GF_DEMO) &&
getPlatform() == Common::kPlatformAmiga) {
sprintf(filename, "%c%d.out", 48 + id, type);
} else if (getGameType() == GType_ELVIRA1 || getGameType() == GType_ELVIRA2) {
sprintf(filename, "%.2d%d.out", id, type);
} else if (getGameType() == GType_PN) {
sprintf(filename, "%c%d.in", id + 48, type);
} else {
sprintf(filename, "%.3d%d.out", id, type);
}
} else {
if (getGameType() == GType_ELVIRA1) {
if (elvira1_soundTable[id] == 0)
return false;
sprintf(filename, "%.2d.SND", elvira1_soundTable[id]);
} else if (getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) {
sprintf(filename, "%.2d%d.VGA", id, type);
} else if (getGameType() == GType_PN) {
sprintf(filename, "%c%d.out", id + 48, type);
} else {
sprintf(filename, "%.3d%d.VGA", id, type);
}
}
in.open(filename);
if (in.isOpen() == false || in.size() == 0) {
return false;
}
dstSize = srcSize = in.size();
if (getGameType() == GType_PN && (getFeatures() & GF_CRUNCHED)) {
Common::Stack<uint32> data;
byte *dataOut = 0;
int dataOutSize = 0;
for (uint i = 0; i < srcSize / 4; ++i)
data.push(in.readUint32BE());
decompressPN(data, dataOut, dataOutSize);
dst = allocBlock (dataOutSize);
memcpy(dst, dataOut, dataOutSize);
delete[] dataOut;
} else if (getGameType() == GType_ELVIRA1 && getFeatures() & GF_DEMO) {
byte *srcBuffer = (byte *)malloc(srcSize);
if (in.read(srcBuffer, srcSize) != srcSize)
error("loadVGASoundFile: Read failed");
dstSize = READ_BE_UINT32(srcBuffer + srcSize - 4);
dst = allocBlock (dstSize);
decrunchFile(srcBuffer, dst, srcSize);
free(srcBuffer);
} else {
dst = allocBlock(dstSize);
if (in.read(dst, dstSize) != dstSize)
error("loadVGASoundFile: Read failed");
}
in.close();
return true;
}
static const char *dimpSoundList[32] = {
"Beep",
"Birth",
"Boiling",
"Burp",
"Cough",
"Die1",
"Die2",
"Fart",
"Inject",
"Killchik",
"Puke",
"Lights",
"Shock",
"Snore",
"Snotty",
"Whip",
"Whistle",
"Work1",
"Work2",
"Yawn",
"And0w",
"And0x",
"And0y",
"And0z",
"And10",
"And11",
"And12",
"And13",
"And14",
"And15",
"And16",
"And17",
};
void AGOSEngine::loadSoundFile(const char* filename) {
Common::File in;
in.open(filename);
if (in.isOpen() == false)
error("loadSound: Can't load %s", filename);
uint32 dstSize = in.size();
byte *dst = (byte *)malloc(dstSize);
if (in.read(dst, dstSize) != dstSize)
error("loadSound: Read failed");
in.close();
_sound->playSfxData(dst, 0, 0, 0);
}
void AGOSEngine::loadSound(uint16 sound, int16 pan, int16 vol, uint16 type) {
byte *dst;
if (getGameId() == GID_DIMP) {
Common::File in;
char filename[15];
assert(sound >= 1 && sound <= 32);
sprintf(filename, "%s.wav", dimpSoundList[sound - 1]);
in.open(filename);
if (in.isOpen() == false)
error("loadSound: Can't load %s", filename);
uint32 dstSize = in.size();
dst = (byte *)malloc(dstSize);
if (in.read(dst, dstSize) != dstSize)
error("loadSound: Read failed");
in.close();
} else if (getFeatures() & GF_ZLIBCOMP) {
char filename[15];
uint32 file, offset, srcSize, dstSize;
if (getPlatform() == Common::kPlatformAmiga) {
loadOffsets((const char*)"sfxindex.dat", _zoneNumber * 22 + sound, file, offset, srcSize, dstSize);
} else {
loadOffsets((const char*)"effects.wav", _zoneNumber * 22 + sound, file, offset, srcSize, dstSize);
}
if (getPlatform() == Common::kPlatformAmiga)
sprintf(filename, "sfx%d.wav", file);
else
sprintf(filename, "effects.wav");
dst = (byte *)malloc(dstSize);
decompressData(filename, dst, offset, srcSize, dstSize);
} else {
if (_curSfxFile == NULL)
return;
dst = _curSfxFile + READ_LE_UINT32(_curSfxFile + sound * 4);
}
if (type == Sound::TYPE_AMBIENT)
_sound->playAmbientData(dst, sound, pan, vol);
else if (type == Sound::TYPE_SFX)
_sound->playSfxData(dst, sound, pan, vol);
else if (type == Sound::TYPE_SFX5)
_sound->playSfx5Data(dst, sound, pan, vol);
}
void AGOSEngine::loadSound(uint16 sound, uint16 freq, uint16 flags) {
byte *dst;
uint32 offs, size = 0;
uint32 rate = 8000;
if (_curSfxFile == NULL)
return;
dst = _curSfxFile;
if (getGameType() == GType_WW) {
uint16 tmp = sound;
while (tmp--) {
size += READ_LE_UINT16(dst) + 4;
dst += READ_LE_UINT16(dst) + 4;
if (size > _curSfxFileSize)
error("loadSound: Reading beyond EOF (%d, %d)", size, _curSfxFileSize);
}
size = READ_LE_UINT16(dst);
offs = 4;
} else if (getGameType() == GType_ELVIRA2) {
while (READ_BE_UINT32(dst + 4) != sound) {
size += 12;
dst += 12;
if (size > _curSfxFileSize)
error("loadSound: Reading beyond EOF (%d, %d)", size, _curSfxFileSize);
}
size = READ_BE_UINT32(dst);
offs = READ_BE_UINT32(dst + 8);
} else {
while (READ_BE_UINT16(dst + 6) != sound) {
size += 12;
dst += 12;
if (size > _curSfxFileSize)
error("loadSound: Reading beyond EOF (%d, %d)", size, _curSfxFileSize);
}
size = READ_BE_UINT16(dst + 2);
offs = READ_BE_UINT32(dst + 8);
}
if (getGameType() == GType_PN) {
if (freq == 0) {
rate = 4600;
} else if (freq == 1) {
rate = 7400;
} else {
rate = 9400;
}
}
// TODO: Handle other sound flags in Amiga/AtariST versions
if (flags == 2 && _sound->isSfxActive()) {
_sound->queueSound(dst + offs, sound, size, rate);
} else {
if (flags == 0)
_sound->stopSfx();
_sound->playRawData(dst + offs, sound, size, rate);
}
}
void AGOSEngine::loadVoice(uint speechId) {
if (getGameType() == GType_PP && speechId == 99) {
_sound->stopVoice();
return;
}
if (getFeatures() & GF_ZLIBCOMP) {
char filename[15];
uint32 file, offset, srcSize, dstSize;
if (getPlatform() == Common::kPlatformAmiga) {
loadOffsets((const char*)"spindex.dat", speechId, file, offset, srcSize, dstSize);
} else {
loadOffsets((const char*)"speech.wav", speechId, file, offset, srcSize, dstSize);
}
// Voice segment doesn't exist
if (offset == 0xFFFFFFFF && srcSize == 0xFFFFFFFF && dstSize == 0xFFFFFFFF) {
debug(0, "loadVoice: speechId %d removed", speechId);
return;
}
if (getPlatform() == Common::kPlatformAmiga)
sprintf(filename, "sp%d.wav", file);
else
sprintf(filename, "speech.wav");
byte *dst = (byte *)malloc(dstSize);
decompressData(filename, dst, offset, srcSize, dstSize);
_sound->playVoiceData(dst, speechId);
} else {
_sound->playVoice(speechId);
}
}
} // End of namespace AGOS