TINSEL: Add basic PSX SEQ playback support

Instrument bank support is not implemented yet
This commit is contained in:
Matthew Hoops 2012-05-03 12:18:10 -04:00
parent b93b17e7f4
commit 76dfbbede4
3 changed files with 89 additions and 13 deletions

View File

@ -131,11 +131,6 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
g_currentMidi = dwFileOffset;
g_currentLoop = bLoop;
// Tinsel V1 PSX uses a different music format, so i
// disable it here.
// TODO: Maybe this should be moved to a better place...
if (TinselV1PSX) return false;
if (_vm->_config->_musicVolume != 0) {
bool mute = false;
if (ConfMan.hasKey("mute"))
@ -231,14 +226,14 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
_vm->_midiMusic->send(0x7F07B0 | 13);
}
_vm->_midiMusic->playXMIDI(g_midiBuffer.pDat, dwSeqLen, bLoop);
_vm->_midiMusic->playMIDI(dwSeqLen, bLoop);
// Store the length
//dwLastSeqLen = dwSeqLen;
} else {
// dwFileOffset == dwLastMidiIndex
_vm->_midiMusic->stop();
_vm->_midiMusic->playXMIDI(g_midiBuffer.pDat, dwSeqLen, bLoop);
_vm->_midiMusic->playMIDI(dwSeqLen, bLoop);
}
return true;
@ -314,8 +309,7 @@ void OpenMidiFiles() {
Common::File midiStream;
// Demo version has no midi file
// Also, Discworld PSX uses still unsupported psx SEQ format for music...
if ((_vm->getFeatures() & GF_DEMO) || (TinselVersion == TINSEL_V2) || TinselV1PSX)
if ((_vm->getFeatures() & GF_DEMO) || (TinselVersion == TINSEL_V2))
return;
if (g_midiBuffer.pDat)
@ -412,7 +406,7 @@ void MidiMusicPlayer::send(uint32 b) {
}
}
void MidiMusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) {
void MidiMusicPlayer::playMIDI(uint32 size, bool loop) {
Common::StackLock lock(_mutex);
if (_isPlaying)
@ -420,6 +414,13 @@ void MidiMusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) {
stop();
if (TinselV1PSX)
playSEQ(size, loop);
else
playXMIDI(size, loop);
}
void MidiMusicPlayer::playXMIDI(uint32 size, bool loop) {
// It seems like not all music (the main menu music, for instance) set
// all the instruments explicitly. That means the music will sound
// different, depending on which music played before it. This appears
@ -433,7 +434,78 @@ void MidiMusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) {
// Load XMID resource data
MidiParser *parser = MidiParser::createParser_XMIDI();
if (parser->loadMusic(midiData, size)) {
if (parser->loadMusic(g_midiBuffer.pDat, size)) {
parser->setTrack(0);
parser->setMidiDriver(this);
parser->setTimerRate(getBaseTempo());
parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
parser->property(MidiParser::mpSendSustainOffOnNotesOff, 1);
_parser = parser;
_isLooping = loop;
_isPlaying = true;
} else {
delete parser;
}
}
void MidiMusicPlayer::playSEQ(uint32 size, bool loop) {
// MIDI.DAT holds the file names in DW1 PSX
Common::String baseName((char *)g_midiBuffer.pDat, size);
Common::String seqName = baseName + ".SEQ";
// TODO: Load the instrument bank (<baseName>.VB and <baseName>.VH)
Common::File seqFile;
if (!seqFile.open(seqName))
error("Failed to open SEQ file '%s'", seqName.c_str());
if (seqFile.readUint32LE() != MKTAG('S', 'E', 'Q', 'p'))
error("Failed to find SEQp tag");
// Make sure we don't have a SEP file (with multiple SEQ's inside)
if (seqFile.readUint32BE() != 1)
error("Can only play SEQ files, not SEP");
uint16 ppqn = seqFile.readUint16BE();
uint32 tempo = seqFile.readUint16BE() << 8;
tempo |= seqFile.readByte();
/* uint16 beat = */ seqFile.readUint16BE();
// SEQ is directly based on SMF and we'll use that to our advantage here
// and convert to SMF and then use the SMF MidiParser.
// Calculate the SMF size we'll need
uint32 dataSize = seqFile.size() - 15;
uint32 actualSize = dataSize + 7 + 22;
// Resize the buffer if necessary
if (g_midiBuffer.size < actualSize) {
g_midiBuffer.pDat = (byte *)realloc(g_midiBuffer.pDat, actualSize);
assert(g_midiBuffer.pDat);
}
// Now construct the header
WRITE_BE_UINT32(g_midiBuffer.pDat, MKTAG('M', 'T', 'h', 'd'));
WRITE_BE_UINT32(g_midiBuffer.pDat + 4, 6); // header size
WRITE_BE_UINT16(g_midiBuffer.pDat + 8, 0); // type 0
WRITE_BE_UINT16(g_midiBuffer.pDat + 10, 1); // one track
WRITE_BE_UINT16(g_midiBuffer.pDat + 12, ppqn);
WRITE_BE_UINT32(g_midiBuffer.pDat + 14, MKTAG('M', 'T', 'r', 'k'));
WRITE_BE_UINT32(g_midiBuffer.pDat + 18, dataSize + 7); // SEQ data size + tempo change event size
// Add in a fake tempo change event
WRITE_BE_UINT32(g_midiBuffer.pDat + 22, 0x00FF5103); // no delta, meta event, tempo change, param size = 3
WRITE_BE_UINT16(g_midiBuffer.pDat + 26, tempo >> 8);
g_midiBuffer.pDat[28] = tempo & 0xFF;
// Now copy in the rest of the events
seqFile.read(g_midiBuffer.pDat + 29, dataSize);
seqFile.close();
MidiParser *parser = MidiParser::createParser_SMF();
if (parser->loadMusic(g_midiBuffer.pDat, actualSize)) {
parser->setTrack(0);
parser->setMidiDriver(this);
parser->setTimerRate(getBaseTempo());

View File

@ -64,7 +64,7 @@ public:
virtual void setVolume(int volume);
void playXMIDI(byte *midiData, uint32 size, bool loop);
void playMIDI(uint32 size, bool loop);
// void stop();
void pause();
@ -76,6 +76,10 @@ public:
// The original sets the "sequence timing" to 109 Hz, whatever that
// means. The default is 120.
uint32 getBaseTempo() { return _driver ? (109 * _driver->getBaseTempo()) / 120 : 0; }
private:
void playXMIDI(uint32 size, bool loop);
void playSEQ(uint32 size, bool loop);
};
class PCMMusicPlayer : public Audio::AudioStream {

View File

@ -1626,7 +1626,7 @@ static void Play(CORO_PARAM, SCNHANDLE hFilm, int x, int y, bool bComplete, int
*/
static void PlayMidi(CORO_PARAM, SCNHANDLE hMidi, int loop, bool complete) {
// FIXME: This is a workaround for the FIXME below
if (GetMidiVolume() == 0 || TinselV1PSX)
if (GetMidiVolume() == 0)
return;
CORO_BEGIN_CONTEXT;