TINSEL: Add support for enhanced music in the Mac version of DW1

This commit is contained in:
Filippos Karapetis 2013-04-12 19:28:20 +03:00
parent cfeda28885
commit 95f92ed9de
4 changed files with 115 additions and 109 deletions

View file

@ -490,7 +490,7 @@ static const TinselGameDescription gameDescriptions[] = {
},
GID_DW1,
0,
GF_SCNFILES,
GF_SCNFILES | GF_ENHANCED_AUDIO_SUPPORT,
TINSEL_V1,
},

View file

@ -135,10 +135,6 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
if (ConfMan.hasKey("mute"))
mute = ConfMan.getBool("mute");
// The Macintosh version of DW1 uses raw PCM for music
if (TinselV1Mac)
return _vm->_sound->playDW1MacMusic(dwFileOffset);
SetMidiVolume(mute ? 0 : _vm->_config->_musicVolume);
// the index and length of the last tune loaded
@ -191,43 +187,48 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
// move to correct position in the file
midiStream.seek(dwFileOffset, SEEK_SET);
// read the length of the sequence
dwSeqLen = midiStream.readUint32LE();
if (TinselV1Mac) {
// The Macintosh version of DW1 uses raw PCM for music
dwSeqLen = midiStream.readUint32BE();
_vm->_sound->playDW1MacMusic(midiStream, dwSeqLen);
} else {
dwSeqLen = midiStream.readUint32LE();
// make sure buffer is large enough for this sequence
assert(dwSeqLen > 0 && dwSeqLen <= g_midiBuffer.size);
// make sure buffer is large enough for this sequence
assert(dwSeqLen > 0 && dwSeqLen <= g_midiBuffer.size);
// stop any currently playing tune
_vm->_midiMusic->stop();
// stop any currently playing tune
_vm->_midiMusic->stop();
// read the sequence. This needs to be read again before playSEQ() is
// called even if the music is restarting, as playSEQ() reads the file
// name off the buffer itself. However, that function adds SMF headers
// to the buffer, thus if it's read again, the SMF headers will be read
// and the filename will always be 'MThd'.
if (midiStream.read(g_midiBuffer.pDat, dwSeqLen) != dwSeqLen)
error(FILE_IS_CORRUPT, MIDI_FILE);
// read the sequence. This needs to be read again before playSEQ() is
// called even if the music is restarting, as playSEQ() reads the file
// name off the buffer itself. However, that function adds SMF headers
// to the buffer, thus if it's read again, the SMF headers will be read
// and the filename will always be 'MThd'.
if (midiStream.read(g_midiBuffer.pDat, dwSeqLen) != dwSeqLen)
error(FILE_IS_CORRUPT, MIDI_FILE);
midiStream.close();
// WORKAROUND for bug #2820054 "DW1: No intro music at first start on Wii",
// which actually affects all ports, since it's specific to the GRA version.
//
// The GRA version does not seem to set the channel volume at all for the first
// intro track, thus we need to do that here. We only initialize the channels
// used in that sequence. And we are using 127 as default channel volume.
//
// Only in the GRA version dwFileOffset can be "38888", just to be sure, we
// check for the SCN files feature flag not being set though.
if (_vm->getGameID() == GID_DW1 && dwFileOffset == 38888 && !(_vm->getFeatures() & GF_SCNFILES)) {
_vm->_midiMusic->send(0x7F07B0 | 3);
_vm->_midiMusic->send(0x7F07B0 | 5);
_vm->_midiMusic->send(0x7F07B0 | 8);
_vm->_midiMusic->send(0x7F07B0 | 10);
_vm->_midiMusic->send(0x7F07B0 | 13);
}
// WORKAROUND for bug #2820054 "DW1: No intro music at first start on Wii",
// which actually affects all ports, since it's specific to the GRA version.
//
// The GRA version does not seem to set the channel volume at all for the first
// intro track, thus we need to do that here. We only initialize the channels
// used in that sequence. And we are using 127 as default channel volume.
//
// Only in the GRA version dwFileOffset can be "38888", just to be sure, we
// check for the SCN files feature flag not being set though.
if (_vm->getGameID() == GID_DW1 && dwFileOffset == 38888 && !(_vm->getFeatures() & GF_SCNFILES)) {
_vm->_midiMusic->send(0x7F07B0 | 3);
_vm->_midiMusic->send(0x7F07B0 | 5);
_vm->_midiMusic->send(0x7F07B0 | 8);
_vm->_midiMusic->send(0x7F07B0 | 10);
_vm->_midiMusic->send(0x7F07B0 | 13);
_vm->_midiMusic->playMIDI(dwSeqLen, bLoop);
}
_vm->_midiMusic->playMIDI(dwSeqLen, bLoop);
midiStream.close();
return true;
}
@ -281,68 +282,89 @@ void SetMidiVolume(int vol) {
void OpenMidiFiles() {
Common::File midiStream;
// Demo version has no midi file
if (TinselV0 || TinselV2)
return;
if (TinselV0) {
// The early demo version of DW1 doesn't have MIDI
} else if (TinselV2) {
// DW2 uses a different music mechanism
} else if (TinselV1Mac) {
// open MIDI sequence file in binary mode
if (!midiStream.open(MIDI_FILE))
error(CANNOT_FIND_FILE, MIDI_FILE);
// The Macintosh version of DW1 does not use MIDI for music.
// It uses PCM music instead, which is quite big to be preloaded here.
if (TinselV1Mac)
return;
uint32 curTrack = 1;
uint32 songLength = 0;
int32 fileSize = midiStream.size();
if (g_midiBuffer.pDat)
// already allocated
return;
// Init
for (int i = 0; i < ARRAYSIZE(g_midiOffsets); i++)
g_midiOffsets[i] = 0;
// open MIDI sequence file in binary mode
if (!midiStream.open(MIDI_FILE))
error(CANNOT_FIND_FILE, MIDI_FILE);
midiStream.skip(4); // skip file header
// gen length of the largest sequence
g_midiBuffer.size = midiStream.readUint32LE();
if (midiStream.eos() || midiStream.err())
error(FILE_IS_CORRUPT, MIDI_FILE);
while (!midiStream.eos() && !midiStream.err() && midiStream.pos() != fileSize) {
assert(curTrack < ARRAYSIZE(g_midiOffsets));
g_midiOffsets[curTrack] = midiStream.pos();
//debug("%d: %d", curTrack, g_midiOffsets[curTrack]);
if (g_midiBuffer.size) {
// allocate a buffer big enough for the largest MIDI sequence
if ((g_midiBuffer.pDat = (uint8 *)malloc(g_midiBuffer.size)) != NULL) {
// clear out the buffer
memset(g_midiBuffer.pDat, 0, g_midiBuffer.size);
// VMM_lock(midiBuffer.pDat, midiBuffer.size);
} else {
//mSeqHandle = NULL;
songLength = midiStream.readUint32BE();
midiStream.skip(songLength);
curTrack++;
}
midiStream.close();
} else {
if (g_midiBuffer.pDat)
// already allocated
return;
// open MIDI sequence file in binary mode
if (!midiStream.open(MIDI_FILE))
error(CANNOT_FIND_FILE, MIDI_FILE);
// get length of the largest sequence
g_midiBuffer.size = midiStream.readUint32LE();
if (midiStream.eos() || midiStream.err())
error(FILE_IS_CORRUPT, MIDI_FILE);
if (g_midiBuffer.size) {
// allocate a buffer big enough for the largest MIDI sequence
if ((g_midiBuffer.pDat = (uint8 *)malloc(g_midiBuffer.size)) != NULL) {
// clear out the buffer
memset(g_midiBuffer.pDat, 0, g_midiBuffer.size);
}
}
// Now scan through the contents of the MIDI file to find the offset
// of each individual track, in order to create a mapping from MIDI
// offset to track number, for the enhanced MIDI soundtrack.
// The first song is always at position 4. The subsequent ones are
// calculated dynamically.
uint32 curOffset = 4;
uint32 curTrack = 0;
uint32 songLength = 0;
// Init
for (int i = 0; i < ARRAYSIZE(g_midiOffsets); i++)
g_midiOffsets[i] = 0;
while (!midiStream.eos() && !midiStream.err()) {
if (curOffset + (4 * curTrack) >= (uint32)midiStream.size())
break;
assert(curTrack < ARRAYSIZE(g_midiOffsets));
g_midiOffsets[curTrack] = curOffset + (4 * curTrack);
//debug("%d: %d", curTrack, midiOffsets[curTrack]);
songLength = midiStream.readUint32LE();
curOffset += songLength;
midiStream.skip(songLength);
curTrack++;
}
midiStream.close();
}
// Now scan through the contents of the MIDI file to find the offset
// of each individual track, in order to create a mapping from MIDI
// offset to track number, for the enhanced MIDI soundtrack.
// The first song is always at position 4. The subsequent ones are
// calculated dynamically.
uint32 curOffset = 4;
uint32 curTrack = 0;
uint32 songLength = 0;
// Init
for (int i = 0; i < ARRAYSIZE(g_midiOffsets); i++)
g_midiOffsets[i] = 0;
while (!midiStream.eos() && !midiStream.err()) {
if (curOffset + (4 * curTrack) >= (uint32)midiStream.size())
break;
assert(curTrack < ARRAYSIZE(g_midiOffsets));
g_midiOffsets[curTrack] = curOffset + (4 * curTrack);
//debug("%d: %d", curTrack, midiOffsets[curTrack]);
songLength = midiStream.readUint32LE();
curOffset += songLength;
midiStream.skip(songLength);
curTrack++;
}
midiStream.close();
}
void DeleteMidiBuffer() {

View file

@ -177,15 +177,7 @@ bool SoundManager::playSample(int id, Audio::Mixer::SoundType type, Audio::Sound
return true;
}
bool SoundManager::playDW1MacMusic(int dwFileOffset) {
Common::File s;
if (!s.open("midi.dat"))
error(CANNOT_FIND_FILE, "midi.dat");
s.seek(dwFileOffset);
uint32 length = s.readUint32BE();
void SoundManager::playDW1MacMusic(Common::File &s, uint32 length) {
// TODO: It's a bad idea to load the music track in a buffer.
// We should use a SubReadStream instead, and keep midi.dat open.
// However, the track lengths aren't that big (about 1-4MB),
@ -195,28 +187,20 @@ bool SoundManager::playDW1MacMusic(int dwFileOffset) {
// read all of the sample
if (s.read(soundData, length) != length)
error(FILE_IS_CORRUPT, "midi.dat");
error(FILE_IS_CORRUPT, MIDI_FILE);
Common::SeekableReadStream *memStream = new Common::MemoryReadStream(soundData, length);
Audio::SoundHandle *handle = &_channels[kChannelDW1MacMusic].handle;
//_channels[kChannelDW1MacMusic].sampleNum = dwFileOffset;
// Stop any previously playing music track
_vm->_mixer->stopHandle(*handle);
// FIXME: Should set this in a different place ;)
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, _vm->_config->_musicVolume);
// TODO: Compression support (MP3/OGG/FLAC) for midi.dat in DW1 Mac
Audio::RewindableAudioStream *musicStream = Audio::makeRawStream(memStream, 22050, Audio::FLAG_UNSIGNED, DisposeAfterUse::YES);
if (musicStream)
_vm->_mixer->playStream(Audio::Mixer::kMusicSoundType, handle, Audio::makeLoopingAudioStream(musicStream, 0));
s.close();
return true;
}
// playSample for DiscWorld 2

View file

@ -109,7 +109,7 @@ public:
bool playSample(int id, Audio::Mixer::SoundType type, Audio::SoundHandle *handle = 0);
bool playSample(int id, int sub, bool bLooped, int x, int y, int priority,
Audio::Mixer::SoundType type, Audio::SoundHandle *handle = 0);
bool playDW1MacMusic(int dwFileOffset);
void playDW1MacMusic(Common::File &s, uint32 length);
void stopAllSamples(); // Stops any currently playing sample
void stopSpecSample(int id, int sub = 0); // Stops a specific sample