diff options
author | Eugene Sandulenko | 2010-06-15 10:36:10 +0000 |
---|---|---|
committer | Eugene Sandulenko | 2010-06-15 10:36:10 +0000 |
commit | 6903fc7a070e33809ba277256b23fa1963e65db7 (patch) | |
tree | 2aeeda79795c6764eca6c680bf9d6d048f8018b2 /engines | |
parent | 85d8b4f5d848c0e1ec6c7b4ef3e6bdd839b05197 (diff) | |
download | scummvm-rg350-6903fc7a070e33809ba277256b23fa1963e65db7.tar.gz scummvm-rg350-6903fc7a070e33809ba277256b23fa1963e65db7.tar.bz2 scummvm-rg350-6903fc7a070e33809ba277256b23fa1963e65db7.zip |
AGI: Implement FR #2813133.
FR #2813133: "AGI: Proper Tandy 3-Voice/IBM PCjr Sound Support".
Add proper Tandy music. Heavily based on NAGI source, thus attached
its X11 license.
To run it now use -e pcjr. Old one is still default for adlib but
most likely will be changed in the future.
Also lied ground for further separation of different sound generators.
svn-id: r49755
Diffstat (limited to 'engines')
-rw-r--r-- | engines/agi/agi.cpp | 9 | ||||
-rw-r--r-- | engines/agi/module.mk | 1 | ||||
-rw-r--r-- | engines/agi/sound.cpp | 28 | ||||
-rw-r--r-- | engines/agi/sound.h | 25 | ||||
-rwxr-xr-x | engines/agi/sound_pcjr.cpp | 497 | ||||
-rwxr-xr-x | engines/agi/sound_pcjr.h | 112 |
6 files changed, 659 insertions, 13 deletions
diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp index d89b10bb32..2f87d7b8ef 100644 --- a/engines/agi/agi.cpp +++ b/engines/agi/agi.cpp @@ -583,15 +583,18 @@ void AgiEngine::initialize() { } else if (getPlatform() == Common::kPlatformCoCo3) { _soundemu = SOUND_EMU_COCO3; } else { - switch (MidiDriver::detectMusicDriver(MDT_PCSPK | MDT_ADLIB)) { + switch (MidiDriver::detectMusicDriver(MDT_PCSPK | MDT_ADLIB | MDT_MIDI)) { case MD_PCSPK: _soundemu = SOUND_EMU_PC; break; + case MD_PCJR: + _soundemu = SOUND_EMU_PCJR; + break; case MD_ADLIB: - _soundemu = SOUND_EMU_MIDI; + _soundemu = SOUND_EMU_NONE; break; default: - _soundemu = SOUND_EMU_NONE; + _soundemu = SOUND_EMU_MIDI; break; } } diff --git a/engines/agi/module.mk b/engines/agi/module.mk index 4f27447dbd..db640c02b5 100644 --- a/engines/agi/module.mk +++ b/engines/agi/module.mk @@ -32,6 +32,7 @@ MODULE_OBJS := \ sound.o \ sound_2gs.o \ sound_midi.o \ + sound_pcjr.o \ sprite.o \ text.o \ view.o \ diff --git a/engines/agi/sound.cpp b/engines/agi/sound.cpp index c35b23ae55..0a71bcabca 100644 --- a/engines/agi/sound.cpp +++ b/engines/agi/sound.cpp @@ -35,6 +35,7 @@ #include "agi/sound_2gs.h" #include "agi/sound_midi.h" +#include "agi/sound_pcjr.h" namespace Agi { @@ -127,6 +128,15 @@ static int noteToPeriod(int note) { } #endif +int SoundMgr::readBuffer(int16 *buffer, const int numSamples) { + if (_vm->_soundemu == SOUND_EMU_PCJR) + _soundGen->premixerCall(buffer, numSamples); + else + premixerCall(buffer, numSamples / 2); + + return numSamples; +} + void SoundMgr::unloadSound(int resnum) { if (_vm->_game.dirSound[resnum].flags & RES_LOADED) { if (_vm->_game.sounds[resnum]->isPlaying()) { @@ -174,6 +184,8 @@ void SoundMgr::startSound(int resnum, int flag) { case AGI_SOUND_4CHN: if (_vm->_soundemu == SOUND_EMU_MIDI) { _musicPlayer->playMIDI((MIDISound *)_vm->_game.sounds[resnum]); + } else if (_vm->_soundemu == SOUND_EMU_PCJR) { + _soundGen->play(resnum, flag); } else { PCjrSound *pcjrSound = (PCjrSound *) _vm->_game.sounds[resnum]; @@ -212,7 +224,8 @@ void SoundMgr::stopSound() { debugC(3, kDebugLevelSound, "stopSound() --> %d", _playingSound); _endflag = -1; - if (_vm->_soundemu != SOUND_EMU_APPLE2GS) { + + if (_vm->_soundemu != SOUND_EMU_APPLE2GS && _vm->_soundemu != SOUND_EMU_PCJR) { for (i = 0; i < NUM_CHANNELS; i++) stopNote(i); } @@ -229,6 +242,10 @@ void SoundMgr::stopSound() { _musicPlayer->stop(); } + if (_vm->_soundemu == SOUND_EMU_PCJR) { + _soundGen->stop(); + } + _playingSound = -1; } @@ -261,6 +278,9 @@ int SoundMgr::initSound() { break; case SOUND_EMU_MIDI: break; + case SOUND_EMU_PCJR: + _soundGen = new SoundGenPCJr(_vm); + break; } report("Initializing sound:\n"); @@ -281,6 +301,7 @@ int SoundMgr::initSound() { void SoundMgr::deinitSound() { debugC(3, kDebugLevelSound, "()"); + stopSound(); _mixer->stopHandle(_soundHandle); } @@ -617,7 +638,7 @@ void SoundMgr::fillAudio(void *udata, int16 *stream, uint len) { data_available -= len; } -SoundMgr::SoundMgr(AgiBase *agi, Audio::Mixer *pMixer) : _chn() { +SoundMgr::SoundMgr(AgiEngine *agi, Audio::Mixer *pMixer) : _chn() { _vm = agi; _mixer = pMixer; _sampleRate = pMixer->getOutputRate(); @@ -630,6 +651,8 @@ SoundMgr::SoundMgr(AgiBase *agi, Audio::Mixer *pMixer) : _chn() { _disabledMidi = false; _useChorus = true; // FIXME: Currently always true? _midiDriver = 0; + _musicPlayer = 0; + _soundGen = 0; _gsSound = new IIgsSoundMgr; @@ -654,6 +677,7 @@ SoundMgr::~SoundMgr() { free(_sndBuffer); delete _gsSound; + delete _soundGen; delete _musicPlayer; delete _midiDriver; } diff --git a/engines/agi/sound.h b/engines/agi/sound.h index d217d52ac6..001b3e35c2 100644 --- a/engines/agi/sound.h +++ b/engines/agi/sound.h @@ -37,7 +37,7 @@ namespace Agi { #define SOUND_EMU_NONE 0 #define SOUND_EMU_PC 1 -#define SOUND_EMU_TANDY 2 +#define SOUND_EMU_PCJR 2 #define SOUND_EMU_MAC 3 #define SOUND_EMU_AMIGA 4 #define SOUND_EMU_APPLE2GS 5 @@ -126,6 +126,17 @@ struct ChannelInfo { class SoundMgr; +class SoundGen { +public: + SoundGen() {} + virtual ~SoundGen() {} + + virtual void play(int resnum, int flag) = 0; + virtual void stop(void) = 0; + + virtual void premixerCall(int16 *stream, int len) = 0; +}; + /** * AGI sound resource structure. */ @@ -167,7 +178,6 @@ protected: }; class AgiEngine; -class AgiBase; class IIgsSoundMgr; class MusicPlayer; @@ -176,15 +186,12 @@ struct IIgsExeInfo; class SoundMgr : public Audio::AudioStream { public: - SoundMgr(AgiBase *agi, Audio::Mixer *pMixer); + SoundMgr(AgiEngine *agi, Audio::Mixer *pMixer); ~SoundMgr(); virtual void setVolume(uint8 volume); // AudioStream API - int readBuffer(int16 *buffer, const int numSamples) { - premixerCall(buffer, numSamples / 2); - return numSamples; - } + int readBuffer(int16 *buffer, const int numSamples); bool isStereo() const { return false; @@ -200,7 +207,7 @@ public: } int _endflag; - AgiBase *_vm; + AgiEngine *_vm; private: Audio::Mixer *_mixer; @@ -226,6 +233,8 @@ private: void premixerCall(int16 *buf, uint len); void fillAudio(void *udata, int16 *stream, uint len); + SoundGen *_soundGen; + public: void unloadSound(int); void playSound(); diff --git a/engines/agi/sound_pcjr.cpp b/engines/agi/sound_pcjr.cpp new file mode 100755 index 0000000000..f00c4424bc --- /dev/null +++ b/engines/agi/sound_pcjr.cpp @@ -0,0 +1,497 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +/* Heavily based on code from NAGI + * + * COPYRIGHT AND PERMISSION NOTICE + * + * Copyright (c) 2001, 2001, 2002 Nick Sonneveld + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * + */ + +#include "agi/agi.h" +#include "agi/sound.h" +#include "agi/sound_pcjr.h" + +namespace Agi { + +// "fade out" or possibly "dissolve" +// v2.9xx +const int8 dissolveDataV2[] = { + -2, -3, -2, -1, + 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x04, 0x04, 0x04, 0x04, + 0x05, 0x05, 0x05, 0x05, + 0x06, 0x06, 0x06, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x08, + 0x09, 0x09, 0x09, 0x09, + 0x0A, 0x0A, 0x0A, 0x0A, + 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0D, + -100 +}; + +// v3 +const int8 dissolveDataV3[] = { + -2, -3, -2, -1, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x04, 0x04, 0x04, 0x04, 0x04, + 0x05, 0x05, 0x05, 0x05, 0x05, + 0x06, 0x06, 0x06, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x08, + 0x09, 0x09, 0x09, 0x09, + 0x0A, 0x0A, 0x0A, 0x0A, + 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0D, + -100 +}; + + +SoundGenPCJr::SoundGenPCJr(AgiEngine *vm) : _vm(vm) { + _chanAllocated = 10240; // preallocate something which will most likely fit + _chanData = (int16 *)malloc(_chanAllocated << 1); + + // Pick dissolve method + // + // 0 = no dissolve.. just play for as long as it's meant to be played + // this was used in older v2.4 and under games i THINK + // 1 = not used + // 2 = v2.9+ games used a shorter dissolve + // 3 (default) = v3 games used this dissolve pattern.. slightly longer + if (_vm->getVersion() >= 0x3000) + _dissolveMethod = 3; + else if (_vm->getVersion() >= 0x2900) + _dissolveMethod = 2; + else + _dissolveMethod = 0; +} + +SoundGenPCJr::~SoundGenPCJr() { + free(_chanData); +} + +void SoundGenPCJr::play(int resnum, int flag) { + PCjrSound *pcjrSound = (PCjrSound *)_vm->_game.sounds[resnum]; + + for (int i = 0; i < CHAN_MAX; i++) { + _channel[i].data = pcjrSound->getVoicePointer(i % 4); + _channel[i].duration = 0; + _channel[i].avail = 0xffff; + _channel[i].dissolveCount = 0xFFFF; + _channel[i].attenuation = 0; + _channel[i].attenuationCopy = 0; + + _tchannel[i].avail = 1; + _tchannel[i].noteCount = 0; + _tchannel[i].freqCount = 250; + _tchannel[i].freqCountPrev = -1; + _tchannel[i].atten = 0xF; // silence + _tchannel[i].genType = kGenTone; + _tchannel[i].genTypePrev = -1; + } +} + +void SoundGenPCJr::stop(void) { + int i; + + for (i = 0; i < CHAN_MAX ; i++) { + _channel[i].avail = 0; + _tchannel[i].avail = 0; + } +} + +int SoundGenPCJr::volumeCalc(SndGenChan *chan) { + int8 attenuation, dissolveValue; + + const int8 *dissolveData; + + switch (_dissolveMethod) { + case 2: + dissolveData = dissolveDataV2; + break; + case 3: + default: + dissolveData = dissolveDataV3; + break; + } + + assert(chan); + + attenuation = chan->attenuation; + if (attenuation != 0x0F) { // != silence + if (chan->dissolveCount != 0xFFFF) { + dissolveValue = dissolveData[chan->dissolveCount]; + if (dissolveValue == -100) { // if at end of list + chan->dissolveCount = 0xFFFF; + chan->attenuation = chan->attenuationCopy; + attenuation = chan->attenuation; + } else { + chan->dissolveCount++; + + attenuation += dissolveValue; + if (attenuation < 0) + attenuation = 0; + if (attenuation > 0x0F) + attenuation = 0x0F; + + chan->attenuationCopy = attenuation; + + attenuation &= 0x0F; + attenuation += _vm->getvar(vVolume); + if (attenuation > 0x0F) + attenuation = 0x0F; + } + } + //if (computer_type == 2) && (attenuation < 8) + if (attenuation < 8) + attenuation += 2; + } + + return attenuation; +} + +// 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) { + SndGenChan *chan; + const byte *data; + + assert(tone); + assert(ch < CHAN_MAX); + + if (!_vm->getflag(fSoundOn)) + return -1; + + chan = &_channel[ch]; + if (!chan->avail) + return -1; + + while ((chan->duration == 0) && (chan->duration != 0xFFFF)) { + data = chan->data; + + // read the duration of the note + chan->duration = READ_LE_UINT16(data); // duration + + // 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)) { + // only tone channels dissolve + if ((ch != 3) && (_dissolveMethod != 0)) // != noise?? + chan->dissolveCount = 0; + + // attenuation (volume) + chan->attenuation = data[4] & 0xF; + + // frequency + if (ch < (CHAN_MAX - 1)) { + chan->freqCount = (uint16)data[2] & 0x3F; + chan->freqCount <<= 4; + chan->freqCount |= data[3] & 0x0F; + + 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 { + // kill channel + chan->avail = 0; + chan->attenuation = 0x0F; // silent + chan->attenuationCopy = 0x0F; // dunno really + + return -1; + } + + return 0; +} + +// Formulas for noise generator +// bit0 = output + +// noise feedback for white noise mode +#define FB_WNOISE 0x12000 // bit15.d(16bits) = bit0(out) ^ bit2 +//#define FB_WNOISE 0x14000 // bit15.d(16bits) = bit0(out) ^ bit1 +//#define FB_WNOISE 0x28000 // bit16.d(17bits) = bit0(out) ^ bit2 (same to AY-3-8910) +//#define FB_WNOISE 0x50000 // bit17.d(18bits) = bit0(out) ^ bit2 + +// noise feedback for periodic noise mode +// it is correct maybe (it was in the Megadrive sound manual) +//#define FB_PNOISE 0x10000 // 16bit rorate +#define FB_PNOISE 0x08000 + +// noise generator start preset (for periodic noise) +#define NG_PRESET 0x0f35 + +//#define WAVE_HEIGHT (0x7FFF) + +// Volume table. +// +// 2dB = 20*log(a/b) +// 10^(2/20)*b = a; +// value = 0x7fff; +// value /= 1.258925411794; +const int16 volTable[16] = { + 32767, 26027, 20674, 16422, 13044, 10361, 8230, 6537, 5193, 4125, 3276, 2602, 2067, 1642, 1304, 0 +}; + +#define FREQ_DIV 111844 +#define MULT FREQ_DIV + +// fill buff +int SoundGenPCJr::chanGen(int chan, int16 *stream, int len) { + ToneChan *tpcm; + Tone toneNew; + int fillSize; + int retVal; + + tpcm = &_tchannel[chan]; + + retVal = -1; + + while (len > 0) { + if (tpcm->noteCount <= 0) { + // get new tone data + 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; + + // setup counters 'n stuff + // SAMPLE_RATE samples per sec.. tone changes 60 times per sec + tpcm->noteCount = SAMPLE_RATE / 60; + retVal = 0; + } else { + // if it doesn't return an + tpcm->genType = kGenSilence; + tpcm->noteCount = len; + tpcm->avail = 0; + } + } + + // write nothing + if ((tpcm->freqCount == 0) || (tpcm->atten == 0xf)) { + tpcm->genType = kGenSilence; + } + + // find which is smaller.. the buffer or the + fillSize = (tpcm->noteCount <= len) ? tpcm->noteCount : len; + + switch (tpcm->genType) { + case kGenTone: + fillSize = fillSquare(tpcm, stream, fillSize); + break; + case kGenPeriod: + case kGenWhite: + fillSize = fillNoise(tpcm, stream, fillSize); + break; + case kGenSilence: + default: + // fill with whitespace + memset(stream, 0, fillSize * sizeof(int16)); + break; + } + + tpcm->noteCount -= fillSize; + stream += fillSize; + len -= fillSize; + } + + return retVal; +} + +int SoundGenPCJr::fillSquare(ToneChan *t, int16 *buf, int len) { + int count; + + if (t->genType != t->genTypePrev) { + // make sure the freqCount is checked + t->freqCountPrev = -1; + t->sign = 1; + t->genTypePrev = t->genType; + } + + if (t->freqCount != t->freqCountPrev) { + //t->scale = (int)( (double)t->samp->freq*t->freqCount/FREQ_DIV * MULT + 0.5); + t->scale = (SAMPLE_RATE / 2) * t->freqCount; + t->count = t->scale; + t->freqCountPrev = t->freqCount; + } + + count = len; + + while (count > 0) { + *(buf++) = t->sign ? volTable[t->atten] : -volTable[t->atten]; + count--; + + // get next sample + t->count -= MULT; + while (t->count <= 0) { + t->sign ^= 1; + t->count += t->scale; + } + } + + return len; +} + +int SoundGenPCJr::fillNoise(ToneChan *t, int16 *buf, int len) { + int count; + + if (t->genType != t->genTypePrev) { + // make sure the freqCount is checked + t->freqCountPrev = -1; + t->genTypePrev = t->genType; + } + + if (t->freqCount != t->freqCountPrev) { + //t->scale = (int)( (double)t->samp->freq*t->freqCount/FREQ_DIV * MULT + 0.5); + t->scale = (SAMPLE_RATE / 2) * t->freqCount; + t->count = t->scale; + t->freqCountPrev = t->freqCount; + + t->feedback = (t->genType == kGenWhite) ? FB_WNOISE : FB_PNOISE; + // reset noise shifter + t->noiseState = NG_PRESET; + t->sign = t->noiseState & 1; + } + + count = len; + + while (count > 0) { + *(buf++) = t->sign ? volTable[t->atten] : -volTable[t->atten]; + count--; + + // get next sample + t->count -= MULT; + while (t->count <= 0) { + if (t->noiseState & 1) + t->noiseState ^= t->feedback; + + t->noiseState >>= 1; + t->sign = t->noiseState & 1; + t->count += t->scale; + } + } + + return len; +} + +void SoundGenPCJr::premixerCall(int16 *stream, int len) { + int streamCount; + int16 *sPtr, *cPtr; + + if (_chanAllocated < len) { + free(_chanData); + _chanData = (int16 *)malloc(len << 1); + _chanAllocated = len; + } + memset(stream, 0, len << 1); + + assert(stream); + + for (int i = 0; i < CHAN_MAX; i++) { + // get channel data(chan.userdata) + if (chanGen(i, _chanData, len) == 0) { + // divide by number of channels then add to stream + streamCount = len; + sPtr = stream; + cPtr = _chanData; + + while (streamCount--) + *(sPtr++) += *(cPtr++) / CHAN_MAX; + } + } +} + +} // End of namespace Agi diff --git a/engines/agi/sound_pcjr.h b/engines/agi/sound_pcjr.h new file mode 100755 index 0000000000..9ef9aa2385 --- /dev/null +++ b/engines/agi/sound_pcjr.h @@ -0,0 +1,112 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef AGI_SOUND_PCJR_H +#define AGI_SOUND_PCJR_H + +namespace Agi { + +#define CHAN_MAX 4 + +#define SAMPLE_RATE 22050 + +enum GenType { + kGenSilence, + kGenTone, + kGenPeriod, + kGenWhite +}; + +struct SndGenChan { + const byte *data; + uint16 duration; + uint16 avail; // turned on (1) but when the channel's data runs out, it's set to (0) + uint16 dissolveCount; + byte attenuation; + byte attenuationCopy; + + GenType genType; + + // for the sample mixer + int freqCount; +}; + +struct ToneChan { + int avail; + + int noteCount; // length of tone.. duration + + int freqCount; + int freqCountPrev; + int atten; // volume + + GenType genType; + int genTypePrev; + + int count; + int scale; + int sign; + unsigned int noiseState; /* noise generator */ + int feedback; /* noise feedback mask */ +}; + +struct Tone { + int freqCount; + int atten; + GenType type; +}; + +class SoundGenPCJr : public SoundGen { +public: + SoundGenPCJr(AgiEngine *vm); + ~SoundGenPCJr(); + + void play(int resnum, int flag); + void stop(void); + + void premixerCall(int16 *stream, int len); + +private: + int getNextNote(int ch, Tone *tone); + int volumeCalc(SndGenChan *chan); + + int chanGen(int chan, int16 *stream, int len); + + int fillNoise(ToneChan *t, int16 *buf, int len); + int fillSquare(ToneChan *t, int16 *buf, int len); + +private: + AgiEngine *_vm; + SndGenChan _channel[CHAN_MAX]; + ToneChan _tchannel[CHAN_MAX]; + int16 *_chanData; + int _chanAllocated; + + int _dissolveMethod; +}; + +} // End of namespace Agi + +#endif /* AGI_SOUND_PCJR_H */ |