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_pcjr.cpp | 103 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) (limited to 'engines/agi/sound_pcjr.cpp') 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; -- cgit v1.2.3 From 1f680ecbc83f34749911dd45be130b0580eb37a9 Mon Sep 17 00:00:00 2001 From: Jussi Pitkanen Date: Wed, 25 May 2011 18:41:57 +0300 Subject: AGI: Detect the end of V1 sound resources correctly, fixing crashes --- engines/agi/sound_pcjr.cpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'engines/agi/sound_pcjr.cpp') diff --git a/engines/agi/sound_pcjr.cpp b/engines/agi/sound_pcjr.cpp index f5cb5e7e1b..d5c6935139 100644 --- a/engines/agi/sound_pcjr.cpp +++ b/engines/agi/sound_pcjr.cpp @@ -126,6 +126,9 @@ SoundGenPCJr::SoundGenPCJr(AgiEngine *vm, Audio::Mixer *pMixer) : SoundGen(vm, p memset(_tchannel, 0, sizeof(_tchannel)); _mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + + _v1data = NULL; + _v1size = 0; } SoundGenPCJr::~SoundGenPCJr() { @@ -323,6 +326,17 @@ int SoundGenPCJr::getNextNote_v1(int ch, Tone *tone) { static bool fetched[4] = { false, false, false, false }; static int duration = 0; + + byte *data = _v1data; + uint32 len = _v1size; + int reg = 0; + + if (len <= 0 || data == NULL) { + _channel[ch].avail = 0; + _channel[ch].attenuation = 0x0F; + _channel[ch].attenuationCopy = 0x0F; + return -1; + } // Get previously fetched data if possible if (fetched[ch]) { @@ -348,13 +362,6 @@ int SoundGenPCJr::getNextNote_v1(int ch, Tone *tone) 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) { -- cgit v1.2.3 From 24bb5da2288b782cfe0e9546ca811e7dd28d0359 Mon Sep 17 00:00:00 2001 From: Jussi Pitkanen Date: Fri, 10 Jun 2011 19:35:00 +0300 Subject: AGI: Add a layer of abstraction between the sound chip and the two players --- engines/agi/sound_pcjr.cpp | 178 +++++++++++++++++---------------------------- 1 file changed, 67 insertions(+), 111 deletions(-) (limited to 'engines/agi/sound_pcjr.cpp') diff --git a/engines/agi/sound_pcjr.cpp b/engines/agi/sound_pcjr.cpp index d5c6935139..2ca71557ba 100644 --- a/engines/agi/sound_pcjr.cpp +++ b/engines/agi/sound_pcjr.cpp @@ -220,12 +220,12 @@ int SoundGenPCJr::volumeCalc(SndGenChan *chan) { return attenuation; } -int SoundGenPCJr::getNextNote(int ch, Tone *tone) +int SoundGenPCJr::getNextNote(int ch) { if (_vm->getVersion() > 0x2001) - return getNextNote_v2(ch, tone); + return getNextNote_v2(ch); else - return getNextNote_v1(ch, tone); + return getNextNote_v1(ch); return -1; } @@ -234,13 +234,11 @@ int SoundGenPCJr::getNextNote(int ch, Tone *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_v2(int ch, Tone *tone) { +int SoundGenPCJr::getNextNote_v2(int ch) { ToneChan *tpcm; SndGenChan *chan; const byte *data; - assert(tone); assert(ch < CHAN_MAX); if (!_vm->getflag(fSoundOn)) @@ -251,7 +249,7 @@ int SoundGenPCJr::getNextNote_v2(int ch, Tone *tone) { if (!chan->avail) return -1; - while ((chan->duration == 0) && (chan->duration != 0xFFFF)) { + while (chan->duration <= 0) { data = chan->data; // read the duration of the note @@ -259,58 +257,32 @@ int SoundGenPCJr::getNextNote_v2(int ch, Tone *tone) { // if it's 0 then it's not going to be played // if it's 0xFFFF then the channel data has finished. - if ((chan->duration != 0) && (chan->duration != 0xFFFF)) { + if ((chan->duration == 0) || (chan->duration == 0xFFFF)) { tpcm->genTypePrev = -1; tpcm->freqCountPrev = -1; - // only tone channels dissolve - if ((ch != 3) && (_dissolveMethod != 0)) // != noise?? - chan->dissolveCount = 0; + break; + } - // attenuation (volume) - chan->attenuation = data[4] & 0xF; + _tchannel[ch].genTypePrev = -1; + _tchannel[ch].freqCountPrev = -1; - // frequency - if (ch < (CHAN_MAX - 1)) { - chan->freqCount = (uint16)data[2] & 0x3F; - chan->freqCount <<= 4; - chan->freqCount |= data[3] & 0x0F; + // only tone channels dissolve + if ((ch != 3) && (_dissolveMethod != 0)) // != noise?? + chan->dissolveCount = 0; + + // attenuation (volume) + writeData(data[4]); + + // frequency + writeData(data[3]); + writeData(data[2]); - chan->genType = kGenTone; - } else { - int noiseFreq; - - // check for white noise (1) or periodic (0) - chan->genType = (data[3] & 0x04) ? kGenWhite : kGenPeriod; - - noiseFreq = data[3] & 0x03; - - switch (noiseFreq) { - case 0: - chan->freqCount = 32; - break; - case 1: - chan->freqCount = 64; - break; - case 2: - chan->freqCount = 128; - break; - case 3: - chan->freqCount = _channel[2].freqCount * 2; - break; - } - } - } // data now points to the next data seg-a-ment chan->data += 5; } - if (chan->duration != 0xFFFF) { - tone->freqCount = chan->freqCount; - tone->atten = volumeCalc(chan); // calc volume, sent vol is different from saved vol - tone->type = chan->genType; - chan->duration--; - } else { + if (chan->duration == 0xFFFF) { // kill channel chan->avail = 0; chan->attenuation = 0x0F; // silent @@ -319,17 +291,17 @@ int SoundGenPCJr::getNextNote_v2(int ch, Tone *tone) { return -1; } + chan->duration--; + return 0; } -int SoundGenPCJr::getNextNote_v1(int ch, Tone *tone) -{ - static bool fetched[4] = { false, false, false, false }; +int SoundGenPCJr::getNextNote_v1(int ch) { + static int channels = 4; static int duration = 0; byte *data = _v1data; uint32 len = _v1size; - int reg = 0; if (len <= 0 || data == NULL) { _channel[ch].avail = 0; @@ -339,60 +311,22 @@ int SoundGenPCJr::getNextNote_v1(int ch, Tone *tone) } // 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; - + if (channels > 0) { + channels--; return 0; } + channels = 4; // 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 - 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; - } - + writeData(*data); data++; len--; } @@ -402,13 +336,43 @@ int SoundGenPCJr::getNextNote_v1(int ch, Tone *tone) _v1data = data; _v1size = len; - tone->freqCount = _channel[ch].freqCount; - tone->atten = _channel[ch].attenuation; - tone->type = _channel[ch].genType; - return 0; } +void SoundGenPCJr::writeData(uint8 val) { + static int reg = 0; + + debugC(5, kDebugLevelSound, "writeData(%.2X)", val); + + if ((val & 0x90) == 0x90) { + reg = (val >> 5) & 0x3; + _channel[reg].attenuation = val & 0xF; + } else if ((val & 0xF0) == 0xE0) { + _channel[3].genType = (val & 0x4) ? kGenWhite : kGenPeriod; + int noiseFreq = val & 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 (val & 0x80) { + reg = (val >> 5) & 0x3; + _channel[reg].freqCount = val & 0xF; + _channel[reg].genType = kGenTone; + } else { + _channel[reg].freqCount |= (val & 0x3F) << 4; + } +} + // Formulas for noise generator // bit0 = output @@ -444,7 +408,6 @@ const int16 volTable[16] = { // fill buff int SoundGenPCJr::chanGen(int chan, int16 *stream, int len) { ToneChan *tpcm; - Tone toneNew; int fillSize; int retVal; @@ -452,20 +415,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; - if ((tpcm->avail) && (getNextNote(chan, &toneNew) == 0)) { - tpcm->atten = toneNew.atten; - tpcm->freqCount = toneNew.freqCount; - tpcm->genType = toneNew.type; + if ((tpcm->avail) && (getNextNote(chan) == 0)) { + tpcm->atten = _channel[chan].attenuation; + tpcm->freqCount = _channel[chan].freqCount; + tpcm->genType = _channel[chan].genType; // setup counters 'n stuff // SAMPLE_RATE samples per sec.. tone changes 60 times per sec -- cgit v1.2.3 From 7a80c4cdb39f003fb893361dde25d40ecd08101a Mon Sep 17 00:00:00 2001 From: Jussi Pitkanen Date: Sun, 12 Jun 2011 17:30:03 +0300 Subject: AGI: Fix row duration in V1 SOUND resource player --- engines/agi/sound_pcjr.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'engines/agi/sound_pcjr.cpp') diff --git a/engines/agi/sound_pcjr.cpp b/engines/agi/sound_pcjr.cpp index 2ca71557ba..14525dc845 100644 --- a/engines/agi/sound_pcjr.cpp +++ b/engines/agi/sound_pcjr.cpp @@ -297,7 +297,6 @@ int SoundGenPCJr::getNextNote_v2(int ch) { } int SoundGenPCJr::getNextNote_v1(int ch) { - static int channels = 4; static int duration = 0; byte *data = _v1data; @@ -310,19 +309,12 @@ int SoundGenPCJr::getNextNote_v1(int ch) { return -1; } - // Get previously fetched data if possible - if (channels > 0) { - channels--; - return 0; - } - channels = 4; - - // In the V1 player the default duration for a row is 2 ticks + // In the V1 player the default duration for a row is 3 ticks if (duration > 0) { duration--; return 0; } - duration = 2 * CHAN_MAX; + duration = 3 * CHAN_MAX; // Otherwise fetch a row of data for all channels while (*data) { -- cgit v1.2.3 From de6390dddbf4f73f3adc68585232f4d4bd836e13 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Sun, 14 Aug 2011 13:08:44 -0400 Subject: AGI: Make the sound code use AgiBase instead of AgiEngine In preparation of using the sound code with Winnie --- engines/agi/sound_pcjr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'engines/agi/sound_pcjr.cpp') diff --git a/engines/agi/sound_pcjr.cpp b/engines/agi/sound_pcjr.cpp index 14525dc845..d21baa450f 100644 --- a/engines/agi/sound_pcjr.cpp +++ b/engines/agi/sound_pcjr.cpp @@ -102,7 +102,7 @@ const int8 dissolveDataV3[] = { }; -SoundGenPCJr::SoundGenPCJr(AgiEngine *vm, Audio::Mixer *pMixer) : SoundGen(vm, pMixer) { +SoundGenPCJr::SoundGenPCJr(AgiBase *vm, Audio::Mixer *pMixer) : SoundGen(vm, pMixer) { _chanAllocated = 10240; // preallocate something which will most likely fit _chanData = (int16 *)malloc(_chanAllocated << 1); @@ -207,7 +207,7 @@ int SoundGenPCJr::volumeCalc(SndGenChan *chan) { chan->attenuationCopy = attenuation; attenuation &= 0x0F; - attenuation += _vm->getvar(vVolume); + attenuation += _mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType) / 17; if (attenuation > 0x0F) attenuation = 0x0F; } -- cgit v1.2.3