Objectified the AudioResource code (used for speech and digitized music in CD talkie games)

svn-id: r40880
This commit is contained in:
Filippos Karapetis 2009-05-25 10:30:19 +00:00
parent 5ef58bdfbe
commit d59796fb54
5 changed files with 164 additions and 94 deletions

View File

@ -86,11 +86,6 @@ namespace Sci {
#define _K_SCI1_SOUND_REVERB 19 /* Get/Set */
#define _K_SCI1_SOUND_UPDATE_VOL_PRI 20
Audio::SoundHandle _audioHandle;
uint16 _audioRate;
int16 _lang;
byte *_audioMap;
enum AudioCommands {
// TODO: find the difference between kSci1AudioWPlay and kSci1AudioPlay
kSci1AudioWPlay = 1, /* Plays an audio stream */
@ -999,33 +994,6 @@ reg_t kDoSound(EngineState *s, int funct_nr, int argc, reg_t *argv) {
return kDoSound_SCI0(s, funct_nr, argc, argv);
}
bool findAudEntry(uint16 audioNumber, byte& volume, uint32& offset, uint32& size) {
// AUDIO00X.MAP contains 10-byte entries:
// w nEntry
// dw offset+volume (as in resource.map)
// dw size
// ending with 10 0xFFs
uint16 n;
uint32 off;
if (_audioMap == 0)
return false;
byte *ptr = _audioMap;
while ((n = READ_UINT16(ptr)) != 0xFFFF) {
if (n == audioNumber) {
off = READ_LE_UINT32(ptr + 2);
size = READ_LE_UINT32(ptr + 6);
volume = off >> 28;
offset = off & 0x0FFFFFFF;
return true;
}
ptr += 10;
}
return false;
}
// Used for speech playback in CD games
reg_t kDoAudio(EngineState *s, int funct_nr, int argc, reg_t *argv) {
Audio::Mixer *mixer = g_system->getMixer();
@ -1034,94 +1002,50 @@ reg_t kDoAudio(EngineState *s, int funct_nr, int argc, reg_t *argv) {
switch (UKPV(0)) {
case kSci1AudioWPlay:
case kSci1AudioPlay: {
mixer->stopHandle(_audioHandle);
s->sound.audioResource->stop();
Audio::AudioStream *audioStream = 0;
// Try to load from an external patch file first
Sci::Resource* audioRes = s->resmgr->findResource(kResourceTypeAudio, UKPV(1), 1);
Audio::AudioStream *audioStream = 0;
if (audioRes) {
audioStream = Audio::makeLinearInputStream(audioRes->data, audioRes->size, _audioRate, Audio::Mixer::FLAG_UNSIGNED, 0, 0);
sampleLen = audioRes->size * 60 / _audioRate;
audioStream = s->sound.audioResource->getAudioStream(audioRes, &sampleLen);
} else {
// No patch file found, read it from the audio volume
byte volume;
uint32 offset;
uint32 size;
if (findAudEntry(UKPV(1), volume, offset, size)) {
uint32 start = offset * 1000 / _audioRate;
uint32 duration = size * 1000 / _audioRate;
char filename[40];
sprintf(filename, "AUDIO%03d.%03d", _lang, volume);
// Try to load compressed
audioStream = Audio::AudioStream::openStreamFile(filename, start, duration);
if (!audioStream) {
// Compressed file load failed, try to load original raw data
byte *soundbuff = (byte *)malloc(size);
Common::File* audioFile = new Common::File();
if (audioFile->open(filename)) {
audioFile->seek(offset);
audioFile->read(soundbuff, size);
audioFile->close();
delete audioFile;
audioStream = Audio::makeLinearInputStream(soundbuff, size, _audioRate,
Audio::Mixer::FLAG_AUTOFREE | Audio::Mixer::FLAG_UNSIGNED, 0, 0);
}
}
sampleLen = size * 60 / _audioRate;
}
audioStream = s->sound.audioResource->getAudioStream(UKPV(1), &sampleLen);
}
if (audioStream)
mixer->playInputStream(Audio::Mixer::kSpeechSoundType, &_audioHandle, audioStream);
}
mixer->playInputStream(Audio::Mixer::kSpeechSoundType, s->sound.audioResource->getAudioHandle(), audioStream);
}
return make_reg(0, sampleLen); // return sample length in ticks
case kSci1AudioStop:
mixer->stopHandle(_audioHandle);
s->sound.audioResource->stop();
break;
case kSci1AudioPause:
mixer->pauseHandle(_audioHandle, true);
s->sound.audioResource->pause();
break;
case kSci1AudioResume:
mixer->pauseHandle(_audioHandle, false);
s->sound.audioResource->resume();
break;
case kSci1AudioPosition:
if (mixer->isSoundHandleActive(_audioHandle)) {
return make_reg(0, mixer->getSoundElapsedTime(_audioHandle) * 6 / 100); // return elapsed time in ticks
} else {
return make_reg(0, -1); // Sound finished
}
return make_reg(0, s->sound.audioResource->getAudioPosition());
case kSci1AudioRate:
_audioRate = UKPV(1);
s->sound.audioResource->setAudioRate(UKPV(1));
break;
case kSci1AudioVolume:
mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, UKPV(1));
break;
case kSci1AudioLanguage:
// TODO: CD Audio (used for example in the end credits of KQ6CD)
if (SKPV(1) != -1) {
_lang = UKPV(1);
if (s->sound.audioResource)
delete s->sound.audioResource;
char filename[40];
sprintf(filename, "AUDIO%03d.MAP", _lang);
// The audio resource is freed when freeing all resources
s->sound.audioResource = new AudioResource();
s->sound.audioResource->setAudioLang(SKPV(1));
Common::File* audioMapFile = new Common::File();
if (audioMapFile->open(filename)) {
// TODO: This needs to be freed, right now we got a memory leak here!
_audioMap = new byte[audioMapFile->size()];
audioMapFile->read(_audioMap, audioMapFile->size());
audioMapFile->close();
delete audioMapFile;
} else {
_audioMap = 0;
}
}
break;
default:
warning("kDoAudio: Unhandled case %d", UKPV(0));

View File

@ -1159,4 +1159,113 @@ void ResourceSync::stopSync() {
//syncStarted = false; // not used
}
AudioResource::AudioResource() {
_audioRate = 0;
_lang = 0;
_audioMap = 0;
}
AudioResource::~AudioResource() {
delete _audioMap;
_audioMap = 0;
}
void AudioResource::setAudioLang(int16 lang) {
// TODO: CD Audio (used for example in the end credits of KQ6CD)
if (lang != -1) {
_lang = lang;
char filename[40];
sprintf(filename, "AUDIO%03d.MAP", _lang);
Common::File* audioMapFile = new Common::File();
if (audioMapFile->open(filename)) {
// The audio map is freed in the destructor
_audioMap = new byte[audioMapFile->size()];
audioMapFile->read(_audioMap, audioMapFile->size());
audioMapFile->close();
delete audioMapFile;
} else {
_audioMap = 0;
}
}
}
int AudioResource::getAudioPosition() {
if (g_system->getMixer()->isSoundHandleActive(_audioHandle)) {
return g_system->getMixer()->getSoundElapsedTime(_audioHandle) * 6 / 100; // return elapsed time in ticks
} else {
return -1; // Sound finished
}
}
bool AudioResource::findAudEntry(uint16 audioNumber, byte& volume, uint32& offset, uint32& size) {
// AUDIO00X.MAP contains 10-byte entries:
// w nEntry
// dw offset+volume (as in resource.map)
// dw size
// ending with 10 0xFFs
uint16 n;
uint32 off;
if (_audioMap == 0)
return false;
byte *ptr = _audioMap;
while ((n = READ_UINT16(ptr)) != 0xFFFF) {
if (n == audioNumber) {
off = READ_LE_UINT32(ptr + 2);
size = READ_LE_UINT32(ptr + 6);
volume = off >> 28;
offset = off & 0x0FFFFFFF;
return true;
}
ptr += 10;
}
return false;
}
Audio::AudioStream* AudioResource::getAudioStream(uint16 audioNumber, int* sampleLen) {
Audio::AudioStream *audioStream = 0;
byte volume;
uint32 offset;
uint32 size;
if (findAudEntry(audioNumber, volume, offset, size)) {
uint32 start = offset * 1000 / _audioRate;
uint32 duration = size * 1000 / _audioRate;
char filename[40];
sprintf(filename, "AUDIO%03d.%03d", _lang, volume);
// Try to load compressed
audioStream = Audio::AudioStream::openStreamFile(filename, start, duration);
if (!audioStream) {
// Compressed file load failed, try to load original raw data
byte *soundbuff = (byte *)malloc(size);
Common::File* audioFile = new Common::File();
if (audioFile->open(filename)) {
audioFile->seek(offset);
audioFile->read(soundbuff, size);
audioFile->close();
delete audioFile;
audioStream = Audio::makeLinearInputStream(soundbuff, size, _audioRate,
Audio::Mixer::FLAG_AUTOFREE | Audio::Mixer::FLAG_UNSIGNED, 0, 0);
}
}
*sampleLen = size * 60 / _audioRate;
}
return audioStream;
}
Audio::AudioStream* AudioResource::getAudioStream(Resource* audioRes, int* sampleLen) {
*sampleLen = audioRes->size * 60 / _audioRate;
return Audio::makeLinearInputStream(audioRes->data, audioRes->size, _audioRate, Audio::Mixer::FLAG_UNSIGNED, 0, 0);
}
} // End of namespace Sci

View File

@ -30,6 +30,9 @@
#include "common/file.h"
#include "common/archive.h"
#include "sound/audiostream.h"
#include "sound/mixer.h" // for SoundHandle
#include "sci/engine/vm.h" // for Object
#include "sci/decompressor.h"
@ -299,6 +302,32 @@ protected:
//bool _syncStarted; // not used
};
class AudioResource {
public:
AudioResource();
~AudioResource();
void setAudioRate(uint16 audioRate) { _audioRate = audioRate; }
void setAudioLang(int16 lang);
Audio::SoundHandle* getAudioHandle() { return &_audioHandle; }
int getAudioPosition();
Audio::AudioStream* getAudioStream(uint16 audioNumber, int* sampleLen);
Audio::AudioStream* getAudioStream(Resource* audioRes, int* sampleLen);
void stop() { g_system->getMixer()->stopHandle(_audioHandle); }
void pause() { g_system->getMixer()->pauseHandle(_audioHandle, true); }
void resume() { g_system->getMixer()->pauseHandle(_audioHandle, false); }
private:
Audio::SoundHandle _audioHandle;
uint16 _audioRate;
int16 _lang;
byte *_audioMap;
bool findAudEntry(uint16 audioNumber, byte& volume, uint32& offset, uint32& size);
};
} // End of namespace Sci
#endif // SCI_SCICORE_RESOURCE_H

View File

@ -367,6 +367,7 @@ void sfx_init(sfx_state_t *self, ResourceManager *resmgr, int flags) {
self->flags = flags;
self->debug = 0; /* Disable all debugging by default */
self->soundSync = NULL;
self->audioResource = NULL;
if (flags & SFX_STATE_FLAG_NOSOUND) {
player = NULL;
@ -443,6 +444,12 @@ void sfx_exit(sfx_state_t *self) {
if (strcmp(player->name, "new") == 0)
g_system->getMixer()->stopAll();
// Delete audio resources for CD talkie games
if (self->audioResource) {
delete self->audioResource;
self->audioResource = 0;
}
}
void sfx_suspend(sfx_state_t *self, int suspend) {

View File

@ -55,7 +55,8 @@ struct sfx_state_t {
song_t *song; /* Active song, or start of active song chain */
int suspended; /* Whether we are suspended */
unsigned int debug; /* Debug flags */
ResourceSync *soundSync; /* Used by kDoSync for speech syncing in talkie games */
ResourceSync *soundSync; /* Used by kDoSync for speech syncing in CD talkie games */
AudioResource *audioResource; /* Used for audio resources in CD talkie games */
};
/***********/