diff options
-rw-r--r-- | engines/agi/sound.cpp | 157 | ||||
-rw-r--r-- | engines/agi/sound.h | 26 |
2 files changed, 155 insertions, 28 deletions
diff --git a/engines/agi/sound.cpp b/engines/agi/sound.cpp index 340e61c009..8527d11202 100644 --- a/engines/agi/sound.cpp +++ b/engines/agi/sound.cpp @@ -121,7 +121,7 @@ IIgsSample::IIgsSample(uint8 *data, uint32 len, int resnum, SoundMgr &manager) : bool IIgsEnvelope::read(Common::SeekableReadStream &stream) { for (int segNum = 0; segNum < ENVELOPE_SEGMENT_COUNT; segNum++) { seg[segNum].bp = stream.readByte(); - seg[segNum].inc = stream.readUint16BE(); + seg[segNum].inc = stream.readUint16LE(); } return !stream.ioFailed(); } @@ -138,7 +138,7 @@ bool IIgsWaveInfo::read(Common::SeekableReadStream &stream, bool ignoreAddr) { mode = (packedModeByte >> 1) & 3; // Bits 1-2 halt = (packedModeByte & 1) != 0; // Bit 0 (Converted to boolean) - relPitch = stream.readUint16BE(); + relPitch = stream.readUint16LE(); // Zero the wave address if we want to ignore the wave address info if (ignoreAddr) @@ -264,6 +264,7 @@ bool SoundMgr::finalizeInstruments(Common::SeekableReadStream &uint8Wave) { static int playing; static ChannelInfo chn[NUM_CHANNELS]; +static IIgsChannelInfo IIgsChannel; static int endflag = -1; static int playingSound = -1; static uint8 env; @@ -346,22 +347,31 @@ void SoundMgr::startSound(int resnum, int flag) { _vm->_game.sounds[resnum]->play(); playingSound = resnum; + debugC(3, kDebugLevelSound, "startSound(resnum = %d, flag = %d)", resnum, flag); + switch (type) { -#if 0 - case AGI_SOUND_SAMPLE: - debugC(3, kDebugLevelSound, "IIGS sample"); - smp = (struct SoundIIgsSample *)_vm->_game.sounds[resnum].rdata; - for (i = 0; i < NUM_CHANNELS; i++) { - chn[i].type = type; - chn[i].flags = 0; - chn[i].ins = (int16 *)&_vm->_game.sounds[resnum].rdata[54]; - chn[i].size = ((int)smp->sizeHi << 8) + smp->sizeLo; - chn[i].ptr = &playSample[i]; - chn[i].timer = 0; - chn[i].vol = 0; - chn[i].end = 0; - } + case AGI_SOUND_SAMPLE: { + IIgsChannelInfo &chn = IIgsChannel; + IIgsSample *sampleRes = (IIgsSample *) _vm->_game.sounds[playingSound]; + const IIgsWaveInfo &waveInfo = chn.ins.oscList(0).waves[0]; + const IIgsSampleHeader &header = sampleRes->getHeader(); + + chn.ins = header.instrument; + chn.sample = sampleRes->getSample() + waveInfo.addr; + chn.pos = intToFrac(0); + chn.posAdd = intToFrac(0); + chn.note = intToFrac(header.pitch) + doubleToFrac(waveInfo.relPitch/256.0); + chn.startEnvVol = intToFrac(0); + chn.chanVol = intToFrac(header.volume); + chn.envVol = chn.startEnvVol; + chn.vol = doubleToFrac(fracToDouble(chn.envVol) * fracToDouble(chn.chanVol) / 127.0); + chn.envSeg = intToFrac(0); + chn.loop = (waveInfo.mode == OSC_MODE_LOOP); + chn.size = waveInfo.size - waveInfo.addr; + chn.end = false; break; + } +#if 0 case AGI_SOUND_MIDI: debugC(3, kDebugLevelSound, "IIGS MIDI sequence"); @@ -410,11 +420,19 @@ void SoundMgr::stopSound() { int i; endflag = -1; - for (i = 0; i < NUM_CHANNELS; i++) - stopNote(i); + if (_vm->_soundemu != SOUND_EMU_APPLE2GS) { + for (i = 0; i < NUM_CHANNELS; i++) + stopNote(i); + } if (playingSound != -1) { _vm->_game.sounds[playingSound]->stop(); + + if (_vm->_soundemu == SOUND_EMU_APPLE2GS) { + IIgsChannel.end = true; + IIgsChannel.chanVol = intToFrac(0); + } + playingSound = -1; } } @@ -563,8 +581,13 @@ void SoundMgr::playMidiSound() { } void SoundMgr::playSampleSound() { - playNote(0, 11025 * 10, 200); - playing = 1; + if (_vm->_soundemu != SOUND_EMU_APPLE2GS) { + warning("Trying to play a sample but not using Apple IIGS sound emulation mode"); + return; + } + + if (playingSound != -1) + playing = !IIgsChannel.end; } void SoundMgr::playAgiSound() { @@ -612,16 +635,26 @@ void SoundMgr::playSound() { if (endflag == -1) return; - if (chn[0].type == AGI_SOUND_MIDI) { - /* play_midi_sound (); */ - playing = 0; - } else if (chn[0].type == AGI_SOUND_SAMPLE) { - playSampleSound(); - } else + if (_vm->_soundemu == SOUND_EMU_APPLE2GS) { + if (playingSound != -1) { + if (_vm->_game.sounds[playingSound]->type() == AGI_SOUND_MIDI) { + /* play_midi_sound (); */ + warning("playSound: Trying to play an Apple IIGS MIDI sound. Not yet implemented!"); + playing = 0; + } else if (_vm->_game.sounds[playingSound]->type() == AGI_SOUND_SAMPLE) { + //debugC(3, kDebugLevelSound, "playSound: Trying to play an Apple IIGS sample"); + playSampleSound(); + } + } + } else { + //debugC(3, kDebugLevelSound, "playSound: Trying to play a PCjr 4-channel sound"); playAgiSound(); + } if (!playing) { - for (i = 0; i < NUM_CHANNELS; chn[i++].vol = 0); + if (_vm->_soundemu != SOUND_EMU_APPLE2GS) { + for (i = 0; i < NUM_CHANNELS; chn[i++].vol = 0); + } if (endflag != -1) _vm->setflag(endflag, true); @@ -640,6 +673,71 @@ uint32 SoundMgr::mixSound(void) { memset(sndBuffer, 0, BUFFER_SIZE << 1); + // Handle Apple IIGS sound mixing here + if (_vm->_soundemu == SOUND_EMU_APPLE2GS && playing && playingSound != -1) { + IIgsChannelInfo &chn = IIgsChannel; + IIgsWaveInfo &waveInfo = chn.ins.oscList(0).waves[0]; + + //uint period = noteToPeriod(fracToInt(chn.note + FRAC_HALF)); + //chn.posAdd = ((frac_t) (118600 * 4 / period)) << (FRAC_BITS - 8); + + // Hertz (number of vibrations a second) = 6.875 x 2 ^ ( ( 3 + MIDI_Pitch ) / 12 ) + // From http://www.musicmasterworks.com/WhereMathMeetsMusic.html + //double hertz = 6.875 * pow(SEMITONE, 3 + fracToDouble(chn.note)); + //double hertz = 8.175798915644 * pow(SEMITONE, fracToDouble(chn.note)); + // double step = getRate() / hertz; + // chn.posAdd = doubleToFrac(step); + + double hertz = 1076.0 * pow(SEMITONE, fracToDouble(chn.note)); + chn.posAdd = doubleToFrac(hertz / getRate()); + chn.vol = doubleToFrac(fracToDouble(chn.envVol) * fracToDouble(chn.chanVol) / 127.0); + double tempVol = fracToDouble(chn.vol)/127.0; + + for (i = 0; i < IIGS_BUFFER_SIZE; i++) { + b = chn.sample[fracToInt(chn.pos)]; + // DOESN'T DO MIXING YET! ONLY ONE SAMPLE PER PLAYING! + sndBuffer[i] = (int16) (b * tempVol); + chn.pos += chn.posAdd; + + if (chn.pos >= intToFrac(chn.size)) { + if (chn.loop) { + chn.pos %= intToFrac(chn.size); + // Probably we should loop the envelope too + chn.envSeg = 0; + chn.envVol = chn.startEnvVol; + } else { + chn.pos = chn.chanVol = 0; + chn.end = true; + break; + } + } + } + + if (chn.envSeg <= chn.ins.relseg) { + IIgsEnvelopeSegment &seg = chn.ins.env.seg[chn.envSeg]; + double bufSecLen = IIGS_BUFFER_SIZE / (double) getRate(); + double ticksPerSec = 100; // 1000 is way too much + double bufTickLen = bufSecLen / (1.0/ticksPerSec); + frac_t envVolDelta = doubleToFrac((seg.inc/256.0)*bufTickLen); + if (intToFrac(seg.bp) >= chn.envVol) { + chn.envVol += envVolDelta; + if (chn.envVol >= intToFrac(seg.bp)) { + chn.envVol = intToFrac(seg.bp); + chn.envSeg += 1; + } + } else { + chn.envVol -= envVolDelta; + if (chn.envVol <= intToFrac(seg.bp)) { + chn.envVol = intToFrac(seg.bp); + chn.envSeg += 1; + } + } + } + //chn.envSeg += doubleToFrac(1/100.0); + return IIGS_BUFFER_SIZE; + } /* else ... */ + + // Handle PCjr 4-channel sound mixing here for (c = 0; c < NUM_CHANNELS; c++) { if (!chn[c].vol) continue; @@ -805,6 +903,7 @@ Common::MemoryReadStream *SoundMgr::loadWaveFile(const Common::String &wavePath, "Please report the information on the previous line to the ScummVM team.\n" \ "Using the wave file as it is - music may sound weird", md5str, exeInfo.exePrefix); } + uint8Wave->seek(0); // Seek wave to its start return uint8Wave; } else { // Couldn't read the wave file or it had incorrect size warning("Error loading Apple IIGS wave file (%s), not loading instruments", wavePath.c_str()); @@ -885,6 +984,10 @@ bool SoundMgr::loadInstruments() { // (A zero in the wave file data can end the sample prematurely) // and convert the wave file from 8-bit unsigned to 16-bit signed format. Common::MemoryReadStream *uint8Wave = loadWaveFile(waveFsnode->path(), *exeInfo); + // Seek the wave to its + if (uint8Wave != NULL) + uint8Wave->seek(0); + bool result = uint8Wave != NULL && loadInstrumentHeaders(exeFsnode->path(), *exeInfo) && finalizeInstruments(*uint8Wave) && convertWave(*uint8Wave, g_wave, uint8Wave->size()); diff --git a/engines/agi/sound.h b/engines/agi/sound.h index 708543ec51..e6689b7e90 100644 --- a/engines/agi/sound.h +++ b/engines/agi/sound.h @@ -29,10 +29,12 @@ #include "agi/agi.h" #include "sound/audiostream.h" #include "sound/mixer.h" +#include "common/frac.h" namespace Agi { #define BUFFER_SIZE 410 +#define IIGS_BUFFER_SIZE 200 #define SOUND_EMU_NONE 0 #define SOUND_EMU_PC 1 @@ -50,7 +52,7 @@ namespace Agi { struct IIgsEnvelopeSegment { uint8 bp; - uint16 inc; ///< 8b.8b fixed point, big endian? + uint16 inc; ///< 8b.8b fixed point, very probably little endian }; #define ENVELOPE_SEGMENT_COUNT 8 @@ -112,6 +114,10 @@ struct IIgsOscillatorList { uint count; ///< Oscillator count IIgsOscillator osc[MAX_OSCILLATORS]; ///< The oscillators + /** Indexing operators for easier access to the oscillators. */ + const IIgsOscillator &operator()(uint index) const { return osc[index]; } + IIgsOscillator &operator()(uint index) { return osc[index]; } + /** Reads an Apple IIGS oscillator list from the given stream. */ bool read(Common::SeekableReadStream &stream, uint oscillatorCount, bool ignoreAddr = false); bool finalize(Common::SeekableReadStream &uint8Wave); @@ -173,6 +179,22 @@ struct AgiNote { } }; +struct IIgsChannelInfo { + IIgsInstrumentHeader ins; ///< Instrument info + const int16 *sample; ///< Source sample data (16-bit signed format) + frac_t pos; ///< Current sample position + frac_t posAdd; ///< Current sample position adder (Calculated using note, vibrato etc) + frac_t note; ///< Note + frac_t vol; ///< Current volume (Takes both channel volume and enveloping into account) + frac_t chanVol; ///< Channel volume + frac_t startEnvVol; ///< Starting envelope volume + frac_t envVol; ///< Current envelope volume + uint envSeg; ///< Current envelope segment + uint size; ///< Sample size + bool loop; ///< Should we loop the sample? + bool end; ///< Has the playing ended? +}; + /** * AGI engine sound channel structure. */ @@ -258,6 +280,8 @@ public: IIgsSample(uint8 *data, uint32 len, int resnum, SoundMgr &manager); ~IIgsSample() { delete[] _sample; } virtual uint16 type() { return _header.type; } + const IIgsSampleHeader &getHeader() const { return _header; } + const int16 *getSample() const { return _sample; } protected: IIgsSampleHeader _header; ///< Apple IIGS AGI sample header int16 *_sample; ///< Sample data (16-bit signed format) |