Objectified the AudioResource code (used for speech and digitized music in CD talkie games)
svn-id: r40880
This commit is contained in:
parent
5ef58bdfbe
commit
d59796fb54
5 changed files with 164 additions and 94 deletions
|
@ -86,11 +86,6 @@ namespace Sci {
|
||||||
#define _K_SCI1_SOUND_REVERB 19 /* Get/Set */
|
#define _K_SCI1_SOUND_REVERB 19 /* Get/Set */
|
||||||
#define _K_SCI1_SOUND_UPDATE_VOL_PRI 20
|
#define _K_SCI1_SOUND_UPDATE_VOL_PRI 20
|
||||||
|
|
||||||
Audio::SoundHandle _audioHandle;
|
|
||||||
uint16 _audioRate;
|
|
||||||
int16 _lang;
|
|
||||||
byte *_audioMap;
|
|
||||||
|
|
||||||
enum AudioCommands {
|
enum AudioCommands {
|
||||||
// TODO: find the difference between kSci1AudioWPlay and kSci1AudioPlay
|
// TODO: find the difference between kSci1AudioWPlay and kSci1AudioPlay
|
||||||
kSci1AudioWPlay = 1, /* Plays an audio stream */
|
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);
|
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
|
// Used for speech playback in CD games
|
||||||
reg_t kDoAudio(EngineState *s, int funct_nr, int argc, reg_t *argv) {
|
reg_t kDoAudio(EngineState *s, int funct_nr, int argc, reg_t *argv) {
|
||||||
Audio::Mixer *mixer = g_system->getMixer();
|
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)) {
|
switch (UKPV(0)) {
|
||||||
case kSci1AudioWPlay:
|
case kSci1AudioWPlay:
|
||||||
case kSci1AudioPlay: {
|
case kSci1AudioPlay: {
|
||||||
mixer->stopHandle(_audioHandle);
|
s->sound.audioResource->stop();
|
||||||
|
|
||||||
|
Audio::AudioStream *audioStream = 0;
|
||||||
|
|
||||||
// Try to load from an external patch file first
|
// Try to load from an external patch file first
|
||||||
Sci::Resource* audioRes = s->resmgr->findResource(kResourceTypeAudio, UKPV(1), 1);
|
Sci::Resource* audioRes = s->resmgr->findResource(kResourceTypeAudio, UKPV(1), 1);
|
||||||
Audio::AudioStream *audioStream = 0;
|
|
||||||
|
|
||||||
if (audioRes) {
|
if (audioRes) {
|
||||||
audioStream = Audio::makeLinearInputStream(audioRes->data, audioRes->size, _audioRate, Audio::Mixer::FLAG_UNSIGNED, 0, 0);
|
audioStream = s->sound.audioResource->getAudioStream(audioRes, &sampleLen);
|
||||||
sampleLen = audioRes->size * 60 / _audioRate;
|
|
||||||
} else {
|
} else {
|
||||||
// No patch file found, read it from the audio volume
|
// No patch file found, read it from the audio volume
|
||||||
byte volume;
|
audioStream = s->sound.audioResource->getAudioStream(UKPV(1), &sampleLen);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audioStream)
|
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
|
return make_reg(0, sampleLen); // return sample length in ticks
|
||||||
case kSci1AudioStop:
|
case kSci1AudioStop:
|
||||||
mixer->stopHandle(_audioHandle);
|
s->sound.audioResource->stop();
|
||||||
break;
|
break;
|
||||||
case kSci1AudioPause:
|
case kSci1AudioPause:
|
||||||
mixer->pauseHandle(_audioHandle, true);
|
s->sound.audioResource->pause();
|
||||||
break;
|
break;
|
||||||
case kSci1AudioResume:
|
case kSci1AudioResume:
|
||||||
mixer->pauseHandle(_audioHandle, false);
|
s->sound.audioResource->resume();
|
||||||
break;
|
break;
|
||||||
case kSci1AudioPosition:
|
case kSci1AudioPosition:
|
||||||
if (mixer->isSoundHandleActive(_audioHandle)) {
|
return make_reg(0, s->sound.audioResource->getAudioPosition());
|
||||||
return make_reg(0, mixer->getSoundElapsedTime(_audioHandle) * 6 / 100); // return elapsed time in ticks
|
|
||||||
} else {
|
|
||||||
return make_reg(0, -1); // Sound finished
|
|
||||||
}
|
|
||||||
case kSci1AudioRate:
|
case kSci1AudioRate:
|
||||||
_audioRate = UKPV(1);
|
s->sound.audioResource->setAudioRate(UKPV(1));
|
||||||
break;
|
break;
|
||||||
case kSci1AudioVolume:
|
case kSci1AudioVolume:
|
||||||
mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, UKPV(1));
|
mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, UKPV(1));
|
||||||
break;
|
break;
|
||||||
case kSci1AudioLanguage:
|
case kSci1AudioLanguage:
|
||||||
// TODO: CD Audio (used for example in the end credits of KQ6CD)
|
if (s->sound.audioResource)
|
||||||
if (SKPV(1) != -1) {
|
delete s->sound.audioResource;
|
||||||
_lang = UKPV(1);
|
|
||||||
|
|
||||||
char filename[40];
|
// The audio resource is freed when freeing all resources
|
||||||
sprintf(filename, "AUDIO%03d.MAP", _lang);
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
warning("kDoAudio: Unhandled case %d", UKPV(0));
|
warning("kDoAudio: Unhandled case %d", UKPV(0));
|
||||||
|
|
|
@ -1159,4 +1159,113 @@ void ResourceSync::stopSync() {
|
||||||
//syncStarted = false; // not used
|
//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
|
} // End of namespace Sci
|
||||||
|
|
|
@ -30,6 +30,9 @@
|
||||||
#include "common/file.h"
|
#include "common/file.h"
|
||||||
#include "common/archive.h"
|
#include "common/archive.h"
|
||||||
|
|
||||||
|
#include "sound/audiostream.h"
|
||||||
|
#include "sound/mixer.h" // for SoundHandle
|
||||||
|
|
||||||
#include "sci/engine/vm.h" // for Object
|
#include "sci/engine/vm.h" // for Object
|
||||||
#include "sci/decompressor.h"
|
#include "sci/decompressor.h"
|
||||||
|
|
||||||
|
@ -299,6 +302,32 @@ protected:
|
||||||
//bool _syncStarted; // not used
|
//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
|
} // End of namespace Sci
|
||||||
|
|
||||||
#endif // SCI_SCICORE_RESOURCE_H
|
#endif // SCI_SCICORE_RESOURCE_H
|
||||||
|
|
|
@ -367,6 +367,7 @@ void sfx_init(sfx_state_t *self, ResourceManager *resmgr, int flags) {
|
||||||
self->flags = flags;
|
self->flags = flags;
|
||||||
self->debug = 0; /* Disable all debugging by default */
|
self->debug = 0; /* Disable all debugging by default */
|
||||||
self->soundSync = NULL;
|
self->soundSync = NULL;
|
||||||
|
self->audioResource = NULL;
|
||||||
|
|
||||||
if (flags & SFX_STATE_FLAG_NOSOUND) {
|
if (flags & SFX_STATE_FLAG_NOSOUND) {
|
||||||
player = NULL;
|
player = NULL;
|
||||||
|
@ -443,6 +444,12 @@ void sfx_exit(sfx_state_t *self) {
|
||||||
|
|
||||||
if (strcmp(player->name, "new") == 0)
|
if (strcmp(player->name, "new") == 0)
|
||||||
g_system->getMixer()->stopAll();
|
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) {
|
void sfx_suspend(sfx_state_t *self, int suspend) {
|
||||||
|
|
|
@ -55,7 +55,8 @@ struct sfx_state_t {
|
||||||
song_t *song; /* Active song, or start of active song chain */
|
song_t *song; /* Active song, or start of active song chain */
|
||||||
int suspended; /* Whether we are suspended */
|
int suspended; /* Whether we are suspended */
|
||||||
unsigned int debug; /* Debug flags */
|
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 */
|
||||||
};
|
};
|
||||||
|
|
||||||
/***********/
|
/***********/
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue