diff options
-rw-r--r-- | engines/gob/inter_fascin.cpp | 50 | ||||
-rw-r--r-- | engines/gob/sound/adlib.cpp | 404 | ||||
-rw-r--r-- | engines/gob/sound/adlib.h | 18 | ||||
-rw-r--r-- | engines/gob/sound/sound.cpp | 18 | ||||
-rw-r--r-- | engines/gob/sound/sound.h | 2 |
5 files changed, 381 insertions, 111 deletions
diff --git a/engines/gob/inter_fascin.cpp b/engines/gob/inter_fascin.cpp index bd0b0a94c6..47db857ddd 100644 --- a/engines/gob/inter_fascin.cpp +++ b/engines/gob/inter_fascin.cpp @@ -552,70 +552,58 @@ void Inter_Fascination::oFascin_geUnknown1(OpGobParams ¶ms) { } void Inter_Fascination::oFascin_geUnknown2(OpGobParams ¶ms) { - warning("Fascination Unknown GE Function 2"); - warning("funcLoadInstruments with parameter : 'extasy.tbr'. (Guess)"); - warning("funcLoadMusic with parameter : 'extasy.mdy'. (Guess)"); + _vm->_sound->adlibLoadTbr("extasy.tbr"); + _vm->_sound->adlibLoadMdy("extasy.mdy"); } void Inter_Fascination::oFascin_geUnknown3(OpGobParams ¶ms) { - warning("Fascination Unknown GE Function 3"); - warning("Verify if 'Guess music' is loaded. If yes, call funcPlayMusic. (Guess)"); + _vm->_sound->adlibPlay(); } void Inter_Fascination::oFascin_geUnknown4(OpGobParams ¶ms) { - warning("Fascination Unknown GE Function 4"); - warning("Verify if 'Guess music' is loaded. If yes, call funcStopMusic. (Guess)"); + _vm->_sound->adlibStop(); } void Inter_Fascination::oFascin_geUnknown5(OpGobParams ¶ms) { - warning("Fascination Unknown GE Function 5"); - warning("Verify if 'instruments' are loaded, If so, call mem_free"); - warning("Verify if 'Guess music' is loaded. If yes, call _cleanupMdy"); - warning("Then set _ptrTbr and _ptrMdy to 0"); + _vm->_sound->adlibUnload(); } void Inter_Fascination::oFascin_geUnknown6(OpGobParams ¶ms) { - warning("Fascination Unknown GE Function 6"); - warning("funcLoadInstruments with parameter : 'music1.tbr'. (Guess)"); - warning("funcLoadMusic with parameter : 'music1.mdy'. (Guess)"); + _vm->_sound->adlibLoadTbr("music1.tbr"); + _vm->_sound->adlibLoadMdy("music1.mdy"); } void Inter_Fascination::oFascin_geUnknown7(OpGobParams ¶ms) { - warning("Fascination Unknown GE Function 7"); - warning("funcLoadInstruments with parameter : 'music2.tbr'. (Guess)"); - warning("funcLoadMusic with parameter : 'music2.mdy'. (Guess)"); + _vm->_sound->adlibLoadTbr("music2.tbr"); + _vm->_sound->adlibLoadMdy("music2.mdy"); } void Inter_Fascination::oFascin_geUnknown8(OpGobParams ¶ms) { - warning("Fascination Unknown GE Function 8"); - warning("funcLoadInstruments with parameter : 'music3.tbr'. (Guess)"); - warning("funcLoadMusic with parameter : 'music3.mdy'. (Guess)"); + _vm->_sound->adlibLoadTbr("music3.tbr"); + _vm->_sound->adlibLoadMdy("music3.mdy"); } void Inter_Fascination::oFascin_geUnknown9(OpGobParams ¶ms) { - warning("Fascination Unknown GE Function 9"); - warning("funcLoadInstruments with parameter : 'batt1.tbr'. (Guess)"); - warning("funcLoadMusic with parameter : 'batt1.mdy'. (Guess)"); + _vm->_sound->adlibLoadTbr("batt1.tbr"); + _vm->_sound->adlibLoadMdy("batt1.mdy"); } void Inter_Fascination::oFascin_geUnknown10(OpGobParams ¶ms) { - warning("Fascination Unknown GE Function 10"); - warning("funcLoadInstruments with parameter : 'batt2.tbr'. (Guess)"); - warning("funcLoadMusic with parameter : 'batt2.mdy'. (Guess)"); + _vm->_sound->adlibLoadTbr("batt2.tbr"); + _vm->_sound->adlibLoadMdy("batt2.mdy"); } void Inter_Fascination::oFascin_geUnknown11(OpGobParams ¶ms) { - warning("Fascination Unknown GE Function 11"); - warning("funcLoadInstruments with parameter : 'batt3.tbr'. (Guess)"); - warning("funcLoadMusic with parameter : 'batt3.mdy'. (Guess)"); + _vm->_sound->adlibLoadTbr("batt3.tbr"); + _vm->_sound->adlibLoadMdy("batt3.mdy"); } void Inter_Fascination::oFascin_geUnknown1000(OpGobParams ¶ms) { - warning("Fascination Unknown GE Function 1000 - Load music ?"); + warning("Fascination Unknown GE Function 1000 - Load MOD music"); } void Inter_Fascination::oFascin_geUnknown1001(OpGobParams ¶ms) { - warning("Fascination oFascin_playProtracker - MOD not compatible, ToBeFixed"); + warning("Fascination oFascin_playProtracker - MOD not compatible (sample > 32768), To Be Fixed"); } void Inter_Fascination::oFascin_geUnknown1002(OpGobParams ¶ms) { diff --git a/engines/gob/sound/adlib.cpp b/engines/gob/sound/adlib.cpp index 07481431b6..8ae553ba6d 100644 --- a/engines/gob/sound/adlib.cpp +++ b/engines/gob/sound/adlib.cpp @@ -51,7 +51,8 @@ AdLib::AdLib(Audio::Mixer &mixer) : _mixer(&mixer) { _ended = false; _playing = false; _needFree = false; - + _mdySong = false; + _repCount = -1; _samplesTillPoll = 0; @@ -107,20 +108,29 @@ int AdLib::readBuffer(int16 *buffer, const int numSamples) { if (_ended) { _first = true; _ended = false; - _playPos = _data + 3 + (_data[1] + 1) * 0x38; + if (_mdySong) + _playPos = _data; + else + _playPos = _data + 3 + (_data[1] + 1) * 0x38; + _samplesTillPoll = 0; if (_repCount == -1) { reset(); - setVoices(); + if (_mdySong) + setVoicesTbr(); + else + setVoices(); } else if (_repCount > 0) { _repCount--; reset(); - setVoices(); + if (_mdySong) + setVoicesTbr(); + else + setVoices(); } else _playing = false; } - return numSamples; } @@ -171,6 +181,19 @@ void AdLib::reset() { // Authorize the control of the waveformes writeOPL(0x01, 0x20); + +// _soundMode 1 : Percussive mode. + if (_soundMode == 1) { + writeOPL(0xA6, 0); + writeOPL(0xB6, 0); + writeOPL(0xA7, 0); + writeOPL(0xB7, 0); + writeOPL(0xA8, 0); + writeOPL(0xB8, 0); + +// TODO set the correct frequency for the last 4 percussive voices + + } } void AdLib::setVoices() { @@ -215,6 +238,69 @@ void AdLib::setVoice(byte voice, byte instr, bool set) { } } +void AdLib::setVoicesTbr() { + int i; + byte *timbrePtr; + + timbrePtr = _timbres; + debugC(6, kDebugSound, "TBR version: %X.%X", timbrePtr[0], timbrePtr[1]); + timbrePtr += 2; + + _tbrCount = READ_LE_UINT16(timbrePtr); + debugC(6, kDebugSound, "Timbres counter: %d", _tbrCount); + timbrePtr += 2; + _tbrStart = READ_LE_UINT16(timbrePtr); + + timbrePtr += 2; + for (i = 0; i < _tbrCount ; i++) + { + setVoiceTbr (i, i, true); + } +} + +void AdLib::setVoiceTbr (byte voice, byte instr, bool set) { + int i; + int j; + uint16 strct[27]; + byte channel; + byte *timbrePtr; + char timbreName[10]; + + timbreName[9] = '\0'; + for (j = 0; j < 9; j++) + timbreName[j] = _timbres[6 + j + (instr * 9)]; + debugC(6, kDebugSound, "Loading timbre %s", timbreName); + + // i = 0 : 0 1 2 3 4 5 6 7 8 9 10 11 12 26 + // i = 1 : 13 14 15 16 17 18 19 20 21 22 23 24 25 27 + for (i = 0; i < 2; i++) { + timbrePtr = _timbres + _tbrStart + instr * 0x38 + i * 0x1A; + for (j = 0; j < 27; j++) { + strct[j] = READ_LE_UINT16(timbrePtr); + timbrePtr += 2; + } + channel = _operators[voice] + i * 3; + writeOPL(0xBD, 0x00); + writeOPL(0x08, 0x00); + writeOPL(0x40 | channel, ((strct[0] & 3) << 6) | (strct[8] & 0x3F)); + if (!i) + writeOPL(0xC0 | voice, + ((strct[2] & 7) << 1) | (1 - (strct[12] & 1))); + writeOPL(0x60 | channel, ((strct[3] & 0xF) << 4) | (strct[6] & 0xF)); + writeOPL(0x80 | channel, ((strct[4] & 0xF) << 4) | (strct[7] & 0xF)); + writeOPL(0x20 | channel, ((strct[9] & 1) << 7) | + ((strct[10] & 1) << 6) | ((strct[5] & 1) << 5) | + ((strct[11] & 1) << 4) | (strct[1] & 0xF)); + if (!i) + writeOPL(0xE0 | channel, (strct[26] & 3)); + else { + writeOPL(0xE0 | channel, (strct[14] & 3)); + writeOPL(0x40 | channel, 0); + } + } +} + + void AdLib::setKey(byte voice, byte note, bool on, bool spec) { short freq = 0; short octa = 0; @@ -293,94 +379,194 @@ void AdLib::pollMusic() { byte volume; uint16 tempo; + uint8 i, tempoMult, tempoFrac, ctrlByte1, ctrlByte2, timbre; + if ((_playPos > (_data + _dataSize)) && (_dataSize != 0xFFFFFFFF)) { _ended = true; return; } - // First tempo, we'll ignore it... - if (_first) { - tempo = *(_playPos++); - // Tempo on 2 bytes - if (tempo & 0x80) - tempo = ((tempo & 3) << 8) | *(_playPos++); - } - _first = false; - - // Instruction - instr = *(_playPos++); - channel = instr & 0x0F; - - switch (instr & 0xF0) { - // Note on + Volume - case 0x00: - note = *(_playPos++); - _pollNotes[channel] = note; - setVolume(channel, *(_playPos++)); - setKey(channel, note, true, false); - break; - // Note on - case 0x90: - note = *(_playPos++); - _pollNotes[channel] = note; - setKey(channel, note, true, false); - break; - // Last note off - case 0x80: - note = _pollNotes[channel]; - setKey(channel, note, false, false); - break; - // Frequency on/off - case 0xA0: - note = *(_playPos++); - setKey(channel, note, _notOn[channel], true); - break; - // Volume - case 0xB0: - volume = *(_playPos++); - setVolume(channel, volume); - break; - // Program change - case 0xC0: - setVoice(channel, *(_playPos++), false); - break; - // Special - case 0xF0: - switch (instr & 0x0F) { - case 0xF: // End instruction + if (_mdySong) + { + if (_first) { + for (i = 0; i < 11; i ++) + setVolume(i, 0); + +// TODO : Set pitch range + + _tempo = _basicTempo; + _wait = *(_playPos++); + _first = false; + } + do { + instr = *_playPos; + switch(instr) { + case 0xF8: + _wait = 0xF8; + break; + case 0xFC: _ended = true; _samplesTillPoll = 0; - return; + break; + case 0xF0: + _playPos++; + ctrlByte1 = *(_playPos++); + ctrlByte2 = *(_playPos++); + if (ctrlByte1 != 0x7F || ctrlByte2 != 0) { + _playPos -= 2; + while (*(_playPos++) != 0xF7); + } else { + tempoMult = *(_playPos++); + tempoFrac = *(_playPos++); + _tempo = _basicTempo * tempoMult + (unsigned)(((long)_basicTempo * tempoFrac) >> 7); + _playPos++; + } + _wait = *(_playPos++); + break; default: - warning("Unknown special command in ADL, stopping playback: %X", - instr & 0x0F); + if (instr >= 0x80) { + _playPos++; + } + channel = (int)(instr & 0x0f); + + switch(instr & 0xf0) { + case 0x90: + note = *(_playPos++); + volume = *(_playPos++); + _pollNotes[channel] = note; + setVolume(channel, volume); + setKey(channel, note, true, false); + break; + case 0x80: + _playPos += 2; + note = _pollNotes[channel]; + setKey(channel, note, false, false); + break; + case 0xA0: + setVolume(channel, *(_playPos++)); + break; + case 0xC0: + timbre = *(_playPos++); + setVoiceTbr(channel, timbre, false); + break; + case 0xE0: + printf("Pitch bend not yet implemented\n"); + + note = *(_playPos)++; + note += (unsigned)(*(_playPos++)) << 7; + + break; + case 0xB0: + _playPos += 2; + break; + case 0xD0: + _playPos++; + break; + default: + warning("Bad MIDI instr byte: 0%X", instr); + while ((*_playPos) < 0x80) + _playPos++; + if (*_playPos != 0xF8) + _playPos--; + break; + } //switch instr & 0xF0 + _wait = *(_playPos++); + break; + } //switch instr + } while (_wait == 0); + + if (_wait == 0x78) { + _wait = 0xF0; + if (*_playPos != 0xF8) + _wait += *(_playPos++); + } + _playPos++; + _samplesTillPoll = _wait * (_rate / 1000); + } else { + // First tempo, we'll ignore it... + if (_first) { + tempo = *(_playPos++); + // Tempo on 2 bytes + if (tempo & 0x80) + tempo = ((tempo & 3) << 8) | *(_playPos++); + } + _first = false; + + // Instruction + instr = *(_playPos++); + channel = instr & 0x0F; + + switch (instr & 0xF0) { + // Note on + Volume + case 0x00: + note = *(_playPos++); + _pollNotes[channel] = note; + setVolume(channel, *(_playPos++)); + setKey(channel, note, true, false); + break; + // Note on + case 0x90: + note = *(_playPos++); + _pollNotes[channel] = note; + setKey(channel, note, true, false); + break; + // Last note off + case 0x80: + note = _pollNotes[channel]; + setKey(channel, note, false, false); + break; + // Frequency on/off + case 0xA0: + note = *(_playPos++); + setKey(channel, note, _notOn[channel], true); + break; + // Volume + case 0xB0: + volume = *(_playPos++); + setVolume(channel, volume); + break; + // Program change + case 0xC0: + setVoice(channel, *(_playPos++), false); + break; + // Special + case 0xF0: + switch (instr & 0x0F) { + case 0xF: // End instruction + _ended = true; + _samplesTillPoll = 0; + return; + default: + warning("Unknown special command in ADL, stopping playback: %X", + instr & 0x0F); + _repCount = 0; + _ended = true; + break; + } + break; + default: + warning("Unknown command in ADL, stopping playback: %X", + instr & 0xF0); _repCount = 0; _ended = true; break; - } - break; - default: - warning("Unknown command in ADL, stopping playback: %X", - instr & 0xF0); - _repCount = 0; + } + + // Temporization + tempo = *(_playPos++); + // End tempo + if (tempo == 0xFF) { _ended = true; - break; - } - - // Temporization - tempo = *(_playPos++); - // End tempo - if (tempo == 0xFF) { - _ended = true; - return; + return; + } + // Tempo on 2 bytes + if (tempo & 0x80) + tempo = ((tempo & 3) << 8) | *(_playPos++); + if (!tempo) + tempo ++; + + _samplesTillPoll = tempo * (_rate / 1000); } - // Tempo on 2 bytes - if (tempo & 0x80) - tempo = ((tempo & 3) << 8) | *(_playPos++); - if (!tempo) - tempo ++; - - _samplesTillPoll = tempo * (_rate / 1000); } bool AdLib::load(const char *fileName) { @@ -419,6 +605,64 @@ bool AdLib::load(byte *data, uint32 size, int index) { return true; } +bool AdLib::loadMdy(const char *fileName) { + Common::File song; + byte mdyHeader[70]; + + unload(); + song.open(fileName); + if (!song.isOpen()) + return false; + + _needFree = true; + _mdySong = true; + + song.read(mdyHeader, 70); + + _tickBeat = mdyHeader[36]; + _beatMeasure = mdyHeader[37]; + _totalTick = mdyHeader[38] + (mdyHeader[39] << 8) + (mdyHeader[40] << 16) + (mdyHeader[41] << 24); + _dataSize = mdyHeader[42] + (mdyHeader[43] << 8) + (mdyHeader[44] << 16) + (mdyHeader[45] << 24); + _nrCommand = mdyHeader[46] + (mdyHeader[47] << 8) + (mdyHeader[48] << 16) + (mdyHeader[49] << 24); +// _soundMode is either 0 (melodic) or 1 (percussive) + _soundMode = mdyHeader[58]; + _pitchBendRangeStep = 25*mdyHeader[59]; + _basicTempo = mdyHeader[60] + (mdyHeader[61] << 8); + + if (_pitchBendRangeStep < 25) + _pitchBendRangeStep = 25; + else if (_pitchBendRangeStep > 300) + _pitchBendRangeStep = 300; + + _data = new byte[_dataSize]; + song.read(_data, _dataSize); + song.close(); + + reset(); + _playPos = _data; + + return true; +} + +bool AdLib::loadTbr(const char *fileName) { + Common::File timbres; + + unload(); + timbres.open(fileName); + if (!timbres.isOpen()) + return false; + + _timbresSize = timbres.size(); + _timbres = new byte[_timbresSize]; + timbres.read(_timbres, _timbresSize); + timbres.close(); + + reset(); + setVoicesTbr(); + + return true; +} + void AdLib::unload() { _playing = false; _index = -1; diff --git a/engines/gob/sound/adlib.h b/engines/gob/sound/adlib.h index 4cd83d5883..0a7663bc09 100644 --- a/engines/gob/sound/adlib.h +++ b/engines/gob/sound/adlib.h @@ -51,6 +51,8 @@ public: bool load(const char *fileName); bool load(byte *data, uint32 size, int index = -1); + bool loadMdy(const char *fileName); + bool loadTbr(const char *fileName); void unload(); // AudioStream API @@ -73,8 +75,10 @@ protected: uint32 _rate; byte *_data; + byte *_timbres; byte *_playPos; uint32 _dataSize; + uint32 _timbresSize; short _freqs[25][12]; byte _notes[11]; @@ -90,14 +94,28 @@ protected: bool _first; bool _ended; bool _needFree; + bool _mdySong; int _index; + uint16 _tbrCount; + uint16 _tbrStart; + + unsigned char _wait; + uint8 _tickBeat; + uint8 _beatMeasure; + uint32 _totalTick; + uint32 _nrCommand; + byte _soundMode; + uint16 _pitchBendRangeStep; + uint16 _basicTempo, _tempo; void writeOPL(byte reg, byte val); void setFreqs(); void reset(); void setVoices(); void setVoice(byte voice, byte instr, bool set); + void setVoicesTbr(); + void setVoiceTbr(byte voice, byte instr, bool set); void setKey(byte voice, byte note, bool on, bool spec); void setVolume(byte voice, byte volume); void pollMusic(); diff --git a/engines/gob/sound/sound.cpp b/engines/gob/sound/sound.cpp index f0cf17b989..3401931c8b 100644 --- a/engines/gob/sound/sound.cpp +++ b/engines/gob/sound/sound.cpp @@ -250,6 +250,24 @@ void Sound::adlibUnload() { _adlib->unload(); } +bool Sound::adlibLoadMdy(const char *fileName) { + if (!_adlib) + return false; + + debugC(1, kDebugSound, "Adlib: Loading data (\"%s\")", fileName); + + return _adlib->loadMdy(fileName); +} + +bool Sound::adlibLoadTbr(const char *fileName) { + if (!_adlib) + return false; + + debugC(1, kDebugSound, "Adlib: Loading instruments (\"%s\")", fileName); + + return _adlib->loadTbr(fileName); +} + void Sound::adlibPlayTrack(const char *trackname) { if (!_adlib || _adlib->isPlaying()) return; diff --git a/engines/gob/sound/sound.h b/engines/gob/sound/sound.h index dbdd580ec7..1c37891a50 100644 --- a/engines/gob/sound/sound.h +++ b/engines/gob/sound/sound.h @@ -79,6 +79,8 @@ public: // AdLib bool adlibLoad(const char *fileName); bool adlibLoad(byte *data, uint32 size, int index = -1); + bool adlibLoadMdy(const char *fileName); + bool adlibLoadTbr(const char *fileName); void adlibUnload(); void adlibPlayTrack(const char *trackname); |