aboutsummaryrefslogtreecommitdiff
path: root/engines/gob/sound/adlib.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/gob/sound/adlib.cpp')
-rw-r--r--engines/gob/sound/adlib.cpp522
1 files changed, 423 insertions, 99 deletions
diff --git a/engines/gob/sound/adlib.cpp b/engines/gob/sound/adlib.cpp
index 07481431b6..7ab8e1f8dd 100644
--- a/engines/gob/sound/adlib.cpp
+++ b/engines/gob/sound/adlib.cpp
@@ -39,18 +39,33 @@ const unsigned char AdLib::_volRegNums[] = {
};
AdLib::AdLib(Audio::Mixer &mixer) : _mixer(&mixer) {
+ init();
+}
+
+AdLib::~AdLib() {
+ Common::StackLock slock(_mutex);
+
+ _mixer->stopHandle(_handle);
+ OPLDestroy(_opl);
+ if (_data && _freeData)
+ delete[] _data;
+}
+
+void AdLib::init() {
_index = -1;
_data = 0;
_playPos = 0;
_dataSize = 0;
_rate = _mixer->getOutputRate();
+
_opl = makeAdlibOPL(_rate);
_first = true;
_ended = false;
_playing = false;
- _needFree = false;
+
+ _freeData = false;
_repCount = -1;
_samplesTillPoll = 0;
@@ -63,15 +78,6 @@ AdLib::AdLib(Audio::Mixer &mixer) : _mixer(&mixer) {
this, -1, 255, 0, false, true);
}
-AdLib::~AdLib() {
- Common::StackLock slock(_mutex);
-
- _mixer->stopHandle(_handle);
- OPLDestroy(_opl);
- if (_data && _needFree)
- delete[] _data;
-}
-
int AdLib::readBuffer(int16 *buffer, const int numSamples) {
Common::StackLock slock(_mutex);
int samples;
@@ -107,7 +113,9 @@ int AdLib::readBuffer(int16 *buffer, const int numSamples) {
if (_ended) {
_first = true;
_ended = false;
- _playPos = _data + 3 + (_data[1] + 1) * 0x38;
+
+ rewind();
+
_samplesTillPoll = 0;
if (_repCount == -1) {
reset();
@@ -120,7 +128,6 @@ int AdLib::readBuffer(int16 *buffer, const int numSamples) {
else
_playing = false;
}
-
return numSamples;
}
@@ -173,48 +180,6 @@ void AdLib::reset() {
writeOPL(0x01, 0x20);
}
-void AdLib::setVoices() {
- // Definitions of the 9 instruments
- for (int i = 0; i < 9; i++)
- setVoice(i, i, true);
-}
-
-void AdLib::setVoice(byte voice, byte instr, bool set) {
- int i;
- int j;
- uint16 strct[27];
- byte channel;
- byte *dataPtr;
-
- // 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++) {
- dataPtr = _data + 3 + instr * 0x38 + i * 0x1A;
- for (j = 0; j < 27; j++) {
- strct[j] = READ_LE_UINT16(dataPtr);
- dataPtr += 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));
- if (i && set)
- writeOPL(0x40 | channel, 0);
- }
-}
-
void AdLib::setKey(byte voice, byte note, bool on, bool spec) {
short freq = 0;
short octa = 0;
@@ -278,7 +243,7 @@ void AdLib::setKey(byte voice, byte note, bool on, bool spec) {
writeOPL(0xB0 + voice, (freq >> 8) | (octa << 2) | 0x20 * on);
if (!freq)
- warning("Voice %d, note %02X unknown", voice, note);
+ warning("AdLib: Voice %d, note %02X unknown", voice, note);
}
void AdLib::setVolume(byte voice, byte volume) {
@@ -287,17 +252,102 @@ void AdLib::setVolume(byte voice, byte volume) {
}
void AdLib::pollMusic() {
+ if ((_playPos > (_data + _dataSize)) && (_dataSize != 0xFFFFFFFF)) {
+ _ended = true;
+ return;
+ }
+
+ interpret();
+}
+
+void AdLib::unload() {
+ _playing = false;
+ _index = -1;
+
+ if (_data && _freeData)
+ delete[] _data;
+
+ _freeData = false;
+}
+
+bool AdLib::isPlaying() const {
+ return _playing;
+}
+
+bool AdLib::getRepeating() const {
+ return _repCount != 0;
+}
+
+void AdLib::setRepeating(int32 repCount) {
+ _repCount = repCount;
+}
+
+int AdLib::getIndex() const {
+ return _index;
+}
+
+void AdLib::startPlay() {
+ if (_data) _playing = true;
+}
+
+void AdLib::stopPlay() {
+ Common::StackLock slock(_mutex);
+ _playing = false;
+}
+
+ADLPlayer::ADLPlayer(Audio::Mixer &mixer) : AdLib(mixer) {
+}
+
+ADLPlayer::~ADLPlayer() {
+}
+
+bool ADLPlayer::load(const char *fileName) {
+ Common::File song;
+
+ unload();
+ song.open(fileName);
+ if (!song.isOpen())
+ return false;
+
+ _freeData = true;
+ _dataSize = song.size();
+ _data = new byte[_dataSize];
+ song.read(_data, _dataSize);
+ song.close();
+
+ reset();
+ setVoices();
+ _playPos = _data + 3 + (_data[1] + 1) * 0x38;
+
+ return true;
+}
+
+bool ADLPlayer::load(byte *data, uint32 size, int index) {
+ unload();
+ _repCount = 0;
+
+ _dataSize = size;
+ _data = data;
+ _index = index;
+
+ reset();
+ setVoices();
+ _playPos = _data + 3 + (_data[1] + 1) * 0x38;
+
+ return true;
+}
+
+void ADLPlayer::unload() {
+ AdLib::unload();
+}
+
+void ADLPlayer::interpret() {
unsigned char instr;
byte channel;
byte note;
byte volume;
uint16 tempo;
- if ((_playPos > (_data + _dataSize)) && (_dataSize != 0xFFFFFFFF)) {
- _ended = true;
- return;
- }
-
// First tempo, we'll ignore it...
if (_first) {
tempo = *(_playPos++);
@@ -352,7 +402,7 @@ void AdLib::pollMusic() {
_samplesTillPoll = 0;
return;
default:
- warning("Unknown special command in ADL, stopping playback: %X",
+ warning("ADLPlayer: Unknown special command %X, stopping playback",
instr & 0x0F);
_repCount = 0;
_ended = true;
@@ -360,7 +410,7 @@ void AdLib::pollMusic() {
}
break;
default:
- warning("Unknown command in ADL, stopping playback: %X",
+ warning("ADLPlayer: Unknown command %X, stopping playback",
instr & 0xF0);
_repCount = 0;
_ended = true;
@@ -383,75 +433,349 @@ void AdLib::pollMusic() {
_samplesTillPoll = tempo * (_rate / 1000);
}
-bool AdLib::load(const char *fileName) {
+void ADLPlayer::reset() {
+ AdLib::reset();
+}
+
+void ADLPlayer::rewind() {
+ _playPos = _data + 3 + (_data[1] + 1) * 0x38;
+}
+
+void ADLPlayer::setVoices() {
+ // Definitions of the 9 instruments
+ for (int i = 0; i < 9; i++)
+ setVoice(i, i, true);
+}
+
+void ADLPlayer::setVoice(byte voice, byte instr, bool set) {
+ uint16 strct[27];
+ byte channel;
+ byte *dataPtr;
+
+ // 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 (int i = 0; i < 2; i++) {
+ dataPtr = _data + 3 + instr * 0x38 + i * 0x1A;
+ for (int j = 0; j < 27; j++) {
+ strct[j] = READ_LE_UINT16(dataPtr);
+ dataPtr += 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));
+ if (i && set)
+ writeOPL(0x40 | channel, 0);
+ }
+}
+
+
+MDYPlayer::MDYPlayer(Audio::Mixer &mixer) : AdLib(mixer) {
+ init();
+}
+
+MDYPlayer::~MDYPlayer() {
+}
+
+void MDYPlayer::init() {
+ _soundMode = 0;
+
+ _timbres = 0;
+ _tbrCount = 0;
+ _tbrStart = 0;
+ _timbresSize = 0;
+}
+
+bool MDYPlayer::loadMDY(Common::SeekableReadStream &stream) {
+ unloadMDY();
+
+ _freeData = true;
+
+ byte mdyHeader[70];
+ stream.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];
+ stream.read(_data, _dataSize);
+
+ reset();
+ _playPos = _data;
+
+ return true;
+}
+
+bool MDYPlayer::loadMDY(const char *fileName) {
Common::File song;
- unload();
song.open(fileName);
if (!song.isOpen())
return false;
- _needFree = true;
- _dataSize = song.size();
- _data = new byte[_dataSize];
- song.read(_data, _dataSize);
- song.close();
+ bool loaded = loadMDY(song);
- reset();
- setVoices();
- _playPos = _data + 3 + (_data[1] + 1) * 0x38;
+ song.close();
- return true;
+ return loaded;
}
-bool AdLib::load(byte *data, uint32 size, int index) {
- unload();
- _repCount = 0;
+bool MDYPlayer::loadTBR(Common::SeekableReadStream &stream) {
+ unloadTBR();
- _dataSize = size;
- _data = data;
- _index = index;
+ _timbresSize = stream.size();
+
+ _timbres = new byte[_timbresSize];
+ stream.read(_timbres, _timbresSize);
reset();
setVoices();
- _playPos = _data + 3 + (_data[1] + 1) * 0x38;
return true;
}
-void AdLib::unload() {
- _playing = false;
- _index = -1;
+bool MDYPlayer::loadTBR(const char *fileName) {
+ Common::File timbres;
- if (_data && _needFree)
- delete[] _data;
+ timbres.open(fileName);
+ if (!timbres.isOpen())
+ return false;
+
+ bool loaded = loadTBR(timbres);
- _needFree = false;
+ timbres.close();
+
+ return loaded;
}
-bool AdLib::isPlaying() const {
- return _playing;
+void MDYPlayer::unload() {
+ unloadTBR();
+ unloadMDY();
}
-bool AdLib::getRepeating() const {
- return _repCount != 0;
+void MDYPlayer::unloadMDY() {
+ AdLib::unload();
}
-void AdLib::setRepeating(int32 repCount) {
- _repCount = repCount;
+void MDYPlayer::unloadTBR() {
+ delete[] _timbres;
+
+ _timbres = 0;
+ _timbresSize = 0;
}
-int AdLib::getIndex() const {
- return _index;
+void MDYPlayer::interpret() {
+ unsigned char instr;
+ byte channel;
+ byte note;
+ byte volume;
+ uint8 tempoMult, tempoFrac;
+ uint8 ctrlByte1, ctrlByte2;
+ uint8 timbre;
+
+ if (_first) {
+ for (int i = 0; i < 11; i ++)
+ setVolume(i, 0);
+
+// TODO : Set pitch range
+
+ _tempo = _basicTempo;
+ _wait = *(_playPos++);
+ _first = false;
+ }
+ do {
+ instr = *_playPos;
+// printf("instr 0x%X\n", instr);
+ switch(instr) {
+ case 0xF8:
+ _wait = *(_playPos++);
+ break;
+ case 0xFC:
+ _ended = true;
+ _samplesTillPoll = 0;
+ return;
+ 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:
+ 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++);
+ setVoice(channel, timbre, false);
+ break;
+ case 0xE0:
+ warning("MDYPlayer: Pitch bend not yet implemented");
+
+ note = *(_playPos)++;
+ note += (unsigned)(*(_playPos++)) << 7;
+
+ setKey(channel, note, _notOn[channel], true);
+
+ break;
+ case 0xB0:
+ _playPos += 2;
+ break;
+ case 0xD0:
+ _playPos++;
+ break;
+ default:
+ warning("MDYPlayer: 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 == 0xF8) {
+ _wait = 0xF0;
+ if (*_playPos != 0xF8)
+ _wait += *(_playPos++);
+ }
+// _playPos++;
+ _samplesTillPoll = _wait * (_rate / 1000);
}
-void AdLib::startPlay() {
- if (_data) _playing = true;
+void MDYPlayer::reset() {
+ AdLib::reset();
+
+// _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::stopPlay() {
- Common::StackLock slock(_mutex);
- _playing = false;
+void MDYPlayer::rewind() {
+ _playPos = _data;
+}
+
+void MDYPlayer::setVoices() {
+ 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 (int i = 0; i < _tbrCount ; i++)
+ setVoice(i, i, true);
+}
+
+void MDYPlayer::setVoice(byte voice, byte instr, bool set) {
+ uint16 strct[27];
+ byte channel;
+ byte *timbrePtr;
+ char timbreName[10];
+
+ timbreName[9] = '\0';
+ for (int 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 (int i = 0; i < 2; i++) {
+ timbrePtr = _timbres + _tbrStart + instr * 0x38 + i * 0x1A;
+ for (int j = 0; j < 27; j++) {
+ if (timbrePtr >= (_timbres + _timbresSize)) {
+ warning("MDYPlayer: Instrument %d out of range (%d, %d)", instr,
+ (uint32) (timbrePtr - _timbres), _timbresSize);
+ strct[j] = 0;
+ } else
+ 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);
+ }
+ }
}
} // End of namespace Gob