DIRECTOR: Fix audio lifetime, implement b_puppetSound

This commit is contained in:
Scott Percival 2020-03-21 23:37:20 +08:00
parent 3382e3e965
commit 59d6c5e5fd
9 changed files with 122 additions and 85 deletions

View File

@ -461,6 +461,7 @@ bool RIFXArchive::openStream(Common::SeekableReadStream *stream, uint32 startOff
tag == MKTAG('R', 'T', 'E', '0') || tag == MKTAG('R', 'T', 'E', '0') ||
tag == MKTAG('R', 'T', 'E', '1') || tag == MKTAG('R', 'T', 'E', '1') ||
tag == MKTAG('R', 'T', 'E', '2') || tag == MKTAG('R', 'T', 'E', '2') ||
tag == MKTAG('s', 'n', 'd', ' ') ||
tag == MKTAG('L', 'c', 't', 'x') || tag == MKTAG('L', 'c', 't', 'x') ||
tag == MKTAG('L', 'n', 'a', 'm') || tag == MKTAG('L', 'n', 'a', 'm') ||
tag == MKTAG('L', 's', 'c', 'r')) tag == MKTAG('L', 's', 'c', 'r'))

View File

@ -27,6 +27,7 @@
#include "director/cachedmactext.h" #include "director/cachedmactext.h"
#include "director/cast.h" #include "director/cast.h"
#include "director/score.h" #include "director/score.h"
#include "director/sound.h"
#include "director/stxt.h" #include "director/stxt.h"
namespace Director { namespace Director {

View File

@ -30,10 +30,6 @@ namespace Graphics {
struct Surface; struct Surface;
} }
namespace Audio {
struct SeekableAudioStream;
}
namespace Common { namespace Common {
class SeekableReadStream; class SeekableReadStream;
class ReadStreamEndian; class ReadStreamEndian;
@ -43,6 +39,7 @@ namespace Director {
class Stxt; class Stxt;
class CachedMacText; class CachedMacText;
class SNDDecoder;
class Cast { class Cast {
public: public:
@ -80,7 +77,7 @@ public:
SoundCast(Common::ReadStreamEndian &stream, uint16 version); SoundCast(Common::ReadStreamEndian &stream, uint16 version);
bool _looping; bool _looping;
Audio::SeekableAudioStream *_audio; SNDDecoder *_audio;
}; };
class ShapeCast : public Cast { class ShapeCast : public Cast {

View File

@ -23,11 +23,13 @@
#include "common/system.h" #include "common/system.h"
#include "director/director.h" #include "director/director.h"
#include "director/cast.h"
#include "director/lingo/lingo.h" #include "director/lingo/lingo.h"
#include "director/lingo/lingo-builtins.h" #include "director/lingo/lingo-builtins.h"
#include "director/lingo/lingo-code.h" #include "director/lingo/lingo-code.h"
#include "director/frame.h" #include "director/frame.h"
#include "director/score.h" #include "director/score.h"
#include "director/sound.h"
#include "director/sprite.h" #include "director/sprite.h"
#include "director/stxt.h" #include "director/stxt.h"
@ -1394,11 +1396,33 @@ void LB::b_puppetPalette(int nargs) {
} }
void LB::b_puppetSound(int nargs) { void LB::b_puppetSound(int nargs) {
g_lingo->convertVOIDtoString(0, nargs); if (nargs != 1) {
error("b_puppetSound: expected 1 argument, got %d", nargs);
g_lingo->dropStack(nargs);
return;
}
Score *score = g_director->getCurrentScore();
g_lingo->printSTUBWithArglist("b_puppetSound", nargs); DirectorSound *sound = g_director->getSoundManager();
Datum castMember = g_lingo->pop();
int castId = g_lingo->castIdFetch(castMember);
if (castId == 0) {
sound->stopSound(1);
} else {
Cast *cast = score->_loadedCast->getVal(castId);
if (cast->_type != kCastSound) {
error("b_puppetSound: attempted to play a non-SoundCast cast member");
return;
}
SNDDecoder *sd = ((SoundCast *)cast)->_audio;
if (!sd) {
warning("b_puppetSound: no audio data attached to cast");
return;
}
sound->playStream(*sd->getAudioStream(), 1);
}
g_lingo->dropStack(nargs);
} }
void LB::b_puppetSprite(int nargs) { void LB::b_puppetSprite(int nargs) {
@ -1802,29 +1826,7 @@ void LB::b_cast(int nargs) {
void LB::b_field(int nargs) { void LB::b_field(int nargs) {
Datum d = g_lingo->pop(); Datum d = g_lingo->pop();
int id; d.u.i = g_lingo->castIdFetch(d);
if (!g_director->getCurrentScore()) {
warning("b_field: Assigning to a field in an empty score");
d.u.i = 0;
d.type = INT;
g_lingo->push(d);
return;
}
if (d.type == STRING) {
if (g_director->getCurrentScore()->_castsNames.contains(*d.u.s))
id = g_director->getCurrentScore()->_castsNames[*d.u.s];
else
error("b_field: Reference to non-existent field: %s", d.u.s->c_str());
} else if (d.type == INT || d.type == FLOAT) {
d.toInt();
id = d.u.i;
} else {
error("b_field: Incorrect reference type: %s", d.type2str());
}
d.u.i = id;
d.type = REFERENCE; d.type = REFERENCE;

View File

@ -488,7 +488,7 @@ void Lingo::processIf(int startlabel, int endlabel, int finalElse) {
int Lingo::castIdFetch(Datum &var) { int Lingo::castIdFetch(Datum &var) {
Score *score = _vm->getCurrentScore(); Score *score = _vm->getCurrentScore();
if (!score) { if (!score) {
warning("castFetch: Score is empty"); warning("castIdFetch: Score is empty");
return 0; return 0;
} }
@ -497,15 +497,15 @@ int Lingo::castIdFetch(Datum &var) {
if (score->_castsNames.contains(*var.u.s)) if (score->_castsNames.contains(*var.u.s))
id = score->_castsNames[*var.u.s]; id = score->_castsNames[*var.u.s];
else else
warning("castFetch: reference to non-existent cast member: %s", var.u.s->c_str()); warning("castIdFetch: reference to non-existent cast member: %s", var.u.s->c_str());
} else if (var.type == INT || var.type == FLOAT) { } else if (var.type == INT || var.type == FLOAT) {
var.toInt(); var.toInt();
if (!score->_loadedCast->contains(var.u.i)) if (!score->_loadedCast->contains(var.u.i))
warning("castFetch: reference to non-existent cast ID: %d", var.u.i); warning("castIdFetch: reference to non-existent cast ID: %d", var.u.i);
else else
id = var.u.i; id = var.u.i;
} else { } else {
error("castFetch: was expecting STRING or INT, got %s", var.type2str()); error("castIdFetch: was expecting STRING or INT, got %s", var.type2str());
} }
return id; return id;

View File

@ -698,27 +698,9 @@ void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
Datum Lingo::getTheCast(Datum &id1, int field) { Datum Lingo::getTheCast(Datum &id1, int field) {
Datum d; Datum d;
int id = 0; int id = g_lingo->castIdFetch(id1);
Score *score = _vm->getCurrentScore(); Score *score = _vm->getCurrentScore();
if (!score) {
warning("Lingo::getTheCast(): The cast %d field \"%s\" setting over non-active score", id, field2str(field));
return d;
}
if (id1.type == INT) {
id = id1.u.i;
} else if (id1.type == STRING) {
if (score->_castsNames.contains(*id1.u.s)) {
id = score->_castsNames[*id1.u.s];
} else {
warning("Lingo::getTheCast(): Unknown the cast \"%s\"", id1.u.s->c_str());
}
} else {
warning("Lingo::getTheCast(): Unknown the cast id type: %s", id1.type2str());
return d;
}
// Setting default type // Setting default type
d.type = INT; d.type = INT;

View File

@ -403,7 +403,8 @@ void Score::loadSpriteSounds(bool isSharedCast) {
} }
if (sndData != NULL && soundCast != NULL) { if (sndData != NULL && soundCast != NULL) {
Audio::SeekableAudioStream *audio = makeSNDStream(sndData); SNDDecoder *audio = new SNDDecoder();
audio->loadStream(*sndData);
soundCast->_audio = audio; soundCast->_audio = audio;
} }
} }

View File

@ -97,10 +97,14 @@ void DirectorSound::playMCI(Audio::AudioStream &stream, uint32 from, uint32 to)
void DirectorSound::playStream(Audio::AudioStream &stream, uint8 soundChannel) { void DirectorSound::playStream(Audio::AudioStream &stream, uint8 soundChannel) {
Audio::SeekableAudioStream *seekStream = dynamic_cast<Audio::SeekableAudioStream *>(&stream); Audio::SeekableAudioStream *seekStream = dynamic_cast<Audio::SeekableAudioStream *>(&stream);
playStream(*seekStream, soundChannel);
}
void DirectorSound::playStream(Audio::SeekableAudioStream &stream, uint8 soundChannel) {
if (soundChannel == 1) if (soundChannel == 1)
_mixer->playStream(Audio::Mixer::kSFXSoundType, _sound1, seekStream); _mixer->playStream(Audio::Mixer::kSFXSoundType, _sound1, &stream);
else else
_mixer->playStream(Audio::Mixer::kSFXSoundType, _sound2, seekStream); _mixer->playStream(Audio::Mixer::kSFXSoundType, _sound2, &stream);
} }
bool DirectorSound::isChannelActive(uint8 channelID) { bool DirectorSound::isChannelActive(uint8 channelID) {
@ -115,6 +119,15 @@ bool DirectorSound::isChannelActive(uint8 channelID) {
return false; return false;
} }
void DirectorSound::stopSound(uint8 channelID) {
if (channelID == 1) {
_mixer->stopHandle(*_sound1);
} else if (channelID == 2) {
_mixer->stopHandle(*_sound2);
}
return;
}
void DirectorSound::stopSound() { void DirectorSound::stopSound() {
_mixer->stopHandle(*_sound1); _mixer->stopHandle(*_sound1);
_mixer->stopHandle(*_sound2); _mixer->stopHandle(*_sound2);
@ -125,55 +138,78 @@ void DirectorSound::systemBeep() {
_speaker->play(Audio::PCSpeaker::kWaveFormSquare, 500, 150); _speaker->play(Audio::PCSpeaker::kWaveFormSquare, 500, 150);
} }
SNDDecoder::SNDDecoder() {
_data = nullptr;
_channels = 0;
_size = 0;
_rate = 0;
_flags = 0;
}
SNDDecoder::~SNDDecoder() {
if (_data) {
free(_data);
}
}
bool SNDDecoder::loadStream(Common::SeekableSubReadStreamEndian &stream) {
if (_data) {
free(_data);
_data = nullptr;
}
Audio::SeekableAudioStream *makeSNDStream(Common::SeekableSubReadStreamEndian *stream) {
if (debugChannelSet(5, kDebugLoading)) { if (debugChannelSet(5, kDebugLoading)) {
debugC(5, kDebugLoading, "snd header:"); debugC(5, kDebugLoading, "snd header:");
stream->hexdump(0x4e); stream.hexdump(0x4e);
} }
// unk1 // unk1
for (uint32 i = 0; i < 0x14; i++) { for (uint32 i = 0; i < 0x14; i++) {
stream->readByte(); stream.readByte();
} }
uint16 channels = stream->readUint16(); _channels = stream.readUint16();
if (channels != 1 || channels != 2) { if (!(_channels == 1 || _channels == 2)) {
warning("STUB: loadSpriteSounds: no support for old sound format"); warning("STUB: loadSpriteSounds: no support for old sound format");
return NULL; return false;
} }
uint16 rate = stream->readUint16(); _rate = stream.readUint16();
// unk2 // unk2
for (uint32 i = 0; i < 0x06; i++) { for (uint32 i = 0; i < 0x06; i++) {
stream->readByte(); stream.readByte();
} }
uint32 length = stream->readUint32(); uint32 length = stream.readUint32();
/*uint16 unk3 =*/stream->readUint16(); /*uint16 unk3 =*/stream.readUint16();
/*uint32 length_copy =*/stream->readUint32(); /*uint32 length_copy =*/stream.readUint32();
/*uint8 unk4 =*/stream->readByte(); /*uint8 unk4 =*/stream.readByte();
/*uint8 unk5 =*/stream->readByte(); /*uint8 unk5 =*/stream.readByte();
/*uint16 unk6 =*/stream->readUint16(); /*uint16 unk6 =*/stream.readUint16();
// unk7 // unk7
for (uint32 i = 0; i < 0x12; i++) { for (uint32 i = 0; i < 0x12; i++) {
stream->readByte(); stream.readByte();
} }
uint16 bits = stream->readUint16(); uint16 bits = stream.readUint16();
// unk8 // unk8
for (uint32 i = 0; i < 0x0c; i++) { for (uint32 i = 0; i < 0x0c; i++) {
stream->readByte(); stream.readByte();
} }
byte flags = 0; _flags = 0;
flags |= channels == 2 ? Audio::FLAG_STEREO : 0; _flags |= _channels == 2 ? Audio::FLAG_STEREO : 0;
flags |= bits == 16 ? Audio::FLAG_16BITS : 0; _flags |= bits == 16 ? Audio::FLAG_16BITS : 0;
flags |= bits == 8 ? Audio::FLAG_UNSIGNED : 0; _flags |= bits == 8 ? Audio::FLAG_UNSIGNED : 0;
uint32 size = length * channels * (bits == 16 ? 2 : 1); _size = length * _channels * (bits == 16 ? 2 : 1);
byte *data = (byte *)malloc(size); _data = (byte *)malloc(_size);
assert(data); assert(_data);
stream->read(data, size); stream.read(_data, _size);
return Audio::makeRawStream(data, size, rate, flags); return true;
}
Audio::SeekableAudioStream *SNDDecoder::getAudioStream() {
return Audio::makeRawStream(_data, _size, _rate, _flags, DisposeAfterUse::NO);
} }

View File

@ -50,12 +50,29 @@ public:
void playAIFF(Common::String filename, uint8 channelID); void playAIFF(Common::String filename, uint8 channelID);
void playMCI(Audio::AudioStream &stream, uint32 from, uint32 to); void playMCI(Audio::AudioStream &stream, uint32 from, uint32 to);
void playStream(Audio::AudioStream &stream, uint8 soundChannel); void playStream(Audio::AudioStream &stream, uint8 soundChannel);
void playStream(Audio::SeekableAudioStream &stream, uint8 soundChannel);
void systemBeep(); void systemBeep();
bool isChannelActive(uint8 channelID); bool isChannelActive(uint8 channelID);
void stopSound(uint8 channelID);
void stopSound(); void stopSound();
}; };
Audio::SeekableAudioStream *makeSNDStream(Common::SeekableSubReadStreamEndian *stream);
class SNDDecoder {
public:
SNDDecoder();
~SNDDecoder();
bool loadStream(Common::SeekableSubReadStreamEndian &stream);
Audio::SeekableAudioStream *getAudioStream();
private:
byte *_data;
uint16 _channels;
uint32 _size;
uint16 _rate;
byte _flags;
};
} // End of namespace Director } // End of namespace Director