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.
This commit is contained in:
Jussi Pitkanen 2011-05-25 12:30:44 +03:00 committed by Eugene Sandulenko
parent a6acf42e74
commit ab784944ea
4 changed files with 118 additions and 1 deletions

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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