From ab784944eac1530fa257191ee7b21a154d9f639f Mon Sep 17 00:00:00 2001 From: Jussi Pitkanen Date: Wed, 25 May 2011 12:30:44 +0300 Subject: AGI: Implement note fetch routine for AGI v2.001 sound resources I suspect this is the format for AGI V1 sound resources as well. It is currently implemented by splitting getNextNote() to getNextNote_v2() and getNextNote_v1(). Since the V1 format consists of simple register values to the sound chip in PCjr, this could probably be made more cleanly by refactoring the code to resemble the chip more closely, so that its state is updated by writing to the registers. --- engines/agi/sound.cpp | 9 ++++ engines/agi/sound.h | 2 + engines/agi/sound_pcjr.cpp | 103 ++++++++++++++++++++++++++++++++++++++++++++- engines/agi/sound_pcjr.h | 5 +++ 4 files changed, 118 insertions(+), 1 deletion(-) (limited to 'engines/agi') diff --git a/engines/agi/sound.cpp b/engines/agi/sound.cpp index f2d7af32da..f44b34c70d 100644 --- a/engines/agi/sound.cpp +++ b/engines/agi/sound.cpp @@ -41,6 +41,10 @@ AgiSound *AgiSound::createFromRawResource(uint8 *data, uint32 len, int resnum, S return NULL; uint16 type = READ_LE_UINT16(data); + // For V1 sound resources + if ((type & 0xFF) == 0x01) + return new PCjrSound(data, len, resnum, manager); + switch (type) { // Create a sound object based on the type case AGI_SOUND_SAMPLE: return new IIgsSample(data, len, resnum, manager); @@ -62,6 +66,11 @@ PCjrSound::PCjrSound(uint8 *data, uint32 len, int resnum, SoundMgr &manager) : A _data = data; // Save the resource pointer _len = len; // Save the resource's length _type = READ_LE_UINT16(data); // Read sound resource's type + + // Detect V1 sound resources + if ((_type & 0xFF) == 0x01) + _type = AGI_SOUND_4CHN; + _isValid = (_type == AGI_SOUND_4CHN) && (_data != NULL) && (_len >= 2); if (!_isValid) // Check for errors diff --git a/engines/agi/sound.h b/engines/agi/sound.h index 0ee19878c4..bd175d3c24 100644 --- a/engines/agi/sound.h +++ b/engines/agi/sound.h @@ -122,6 +122,8 @@ public: ~PCjrSound() { free(_data); } virtual uint16 type() { return _type; } const uint8 *getVoicePointer(uint voiceNum); + uint8 *getData() { return _data; } + uint32 getLength() { return _len; } protected: uint8 *_data; ///< Raw sound resource data uint32 _len; ///< Length of the raw sound resource diff --git a/engines/agi/sound_pcjr.cpp b/engines/agi/sound_pcjr.cpp index fdebf16b1a..f5cb5e7e1b 100644 --- a/engines/agi/sound_pcjr.cpp +++ b/engines/agi/sound_pcjr.cpp @@ -153,6 +153,9 @@ void SoundGenPCJr::play(int resnum) { _tchannel[i].genType = kGenTone; _tchannel[i].genTypePrev = -1; } + + _v1data = pcjrSound->getData() + 1; + _v1size = pcjrSound->getLength() - 1; } void SoundGenPCJr::stop(void) { @@ -214,11 +217,22 @@ int SoundGenPCJr::volumeCalc(SndGenChan *chan) { return attenuation; } +int SoundGenPCJr::getNextNote(int ch, Tone *tone) +{ + if (_vm->getVersion() > 0x2001) + return getNextNote_v2(ch, tone); + else + return getNextNote_v1(ch, tone); + + return -1; +} + // read the next channel data.. fill it in *tone // if tone isn't touched.. it should be inited so it just plays silence // return 0 if it's passing more data // return -1 if it's passing nothing (end of data) -int SoundGenPCJr::getNextNote(int ch, Tone *tone) { + +int SoundGenPCJr::getNextNote_v2(int ch, Tone *tone) { ToneChan *tpcm; SndGenChan *chan; const byte *data; @@ -305,6 +319,89 @@ int SoundGenPCJr::getNextNote(int ch, Tone *tone) { return 0; } +int SoundGenPCJr::getNextNote_v1(int ch, Tone *tone) +{ + static bool fetched[4] = { false, false, false, false }; + static int duration = 0; + + // Get previously fetched data if possible + if (fetched[ch]) { + fetched[ch] = false; + + tone->freqCount = _channel[ch].freqCount; + tone->atten = _channel[ch].attenuation; + tone->type = _channel[ch].genType; + + return 0; + } + + // In the V1 player the default duration for a row is 2 ticks + if (duration > 0) { + duration--; + + tone->freqCount = _channel[ch].freqCount; + tone->atten = _channel[ch].attenuation; + tone->type = _channel[ch].genType; + + return 0; + } + duration = 2 * CHAN_MAX; + + // Otherwise fetch a row of data for all channels + byte *data = _v1data; + uint32 len = _v1size; + int reg = 0; + + if (len <= 0 || data == NULL) + return -1; + + fetched[0] = fetched[1] = fetched[2] = fetched[3] = true; + + while (*data) { + if ((*data & 0x90) == 0x90) { + reg = (*data >> 5) & 0x3; + _channel[reg].attenuation = *data & 0xF; + } else if ((*data & 0xF0) == 0xE0) { + _channel[3].genType = (data[3] & 0x4) ? kGenWhite : kGenPeriod; + int noiseFreq = data[3] & 0x03; + switch (noiseFreq) { + case 0: + _channel[3].freqCount = 32; + break; + case 1: + _channel[3].freqCount = 64; + break; + case 2: + _channel[3].freqCount = 128; + break; + case 3: + _channel[3].freqCount = _channel[2].freqCount * 2; + break; + } + } else if (*data & 0x80) { + reg = (*data >> 5) & 0x3; + _channel[reg].freqCount = *data & 0xF; + _channel[reg].genType = kGenTone; + } else { + _channel[reg].freqCount |= (*data & 0x3F) << 4; + } + + data++; + len--; + } + data++; + len--; + + _v1data = data; + _v1size = len; + + tone->freqCount = _channel[ch].freqCount; + tone->atten = _channel[ch].attenuation; + tone->type = _channel[ch].genType; + + return 0; +} + // Formulas for noise generator // bit0 = output @@ -348,9 +445,13 @@ int SoundGenPCJr::chanGen(int chan, int16 *stream, int len) { retVal = -1; + debugC(5, kDebugLevelSound, "chanGen()"); + while (len > 0) { if (tpcm->noteCount <= 0) { // get new tone data + tpcm->avail=1; + debugC(5, kDebugLevelSound, "new note data (avail=%d)", tpcm->avail); toneNew.freqCount = 0; toneNew.atten = 0xF; toneNew.type = kGenTone; diff --git a/engines/agi/sound_pcjr.h b/engines/agi/sound_pcjr.h index 4317e86516..d9ced4ac80 100644 --- a/engines/agi/sound_pcjr.h +++ b/engines/agi/sound_pcjr.h @@ -103,6 +103,8 @@ public: private: int getNextNote(int ch, Tone *tone); + int getNextNote_v2(int ch, Tone *tone); + int getNextNote_v1(int ch, Tone *tone); int volumeCalc(SndGenChan *chan); int chanGen(int chan, int16 *stream, int len); @@ -117,6 +119,9 @@ private: int _chanAllocated; int _dissolveMethod; + + uint8 *_v1data; + uint32 _v1size; }; } // End of namespace Agi -- cgit v1.2.3