From 620c8a5647d1c51a4ca1803acf5924e3099e1d56 Mon Sep 17 00:00:00 2001 From: Gregory Montoir Date: Sat, 19 May 2007 12:06:51 +0000 Subject: added Amiga SoundFx module player code, used by Amiga versions of the following Delphine Software games : Future Wars, Operation Stealth, Cruise for a Corpse and Another World svn-id: r26869 --- sound/mods/paula.h | 2 +- sound/mods/soundfx.cpp | 298 +++++++++++++++++++++++++++++++++++++++++++++++++ sound/mods/soundfx.h | 37 ++++++ sound/module.mk | 1 + 4 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 sound/mods/soundfx.cpp create mode 100644 sound/mods/soundfx.h diff --git a/sound/mods/paula.h b/sound/mods/paula.h index f79ad87466..b0c9bc96b8 100644 --- a/sound/mods/paula.h +++ b/sound/mods/paula.h @@ -41,7 +41,7 @@ public: ~Paula(); bool playing() const { return _playing; } - void setInterruptFreq(int freq) { _intFreq = freq; } + void setInterruptFreq(int freq) { _curInt = _intFreq = freq; } void setPanning(byte voice, byte panning) { assert(voice < NUM_VOICES); _voice[voice].panning = panning; diff --git a/sound/mods/soundfx.cpp b/sound/mods/soundfx.cpp new file mode 100644 index 0000000000..d9edb8b42d --- /dev/null +++ b/sound/mods/soundfx.cpp @@ -0,0 +1,298 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2007 The ScummVM project + * + * 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$ + * + */ + +#include "common/stdafx.h" +#include "common/endian.h" + +#include "sound/mods/paula.h" +#include "sound/mods/soundfx.h" +#include "sound/audiostream.h" + +namespace Audio { + +struct SoundFxInstrument { + uint8 name[23]; + uint16 len; + uint8 finetune; + uint8 volume; + uint16 repeatPos; + uint16 repeatLen; + int8 *data; +}; + +class SoundFx : public Paula { +public: + + enum { + NUM_CHANNELS = 4, + NUM_INSTRUMENTS = 15, + CIA_FREQ = 715909 + }; + + SoundFx(int rate, bool stereo); + virtual ~SoundFx(); + + bool load(Common::SeekableReadStream *data); + void play(); + +protected: + + void handlePattern(int ch, uint32 pat); + void updateEffects(int ch); + void handleTick(); + + void startPaula(); + void stopPaula(); + void setPaulaChannelPeriod(uint8 channel, int16 period); + void setPaulaChannelVolume(uint8 channel, uint8 volume); + void enablePaulaChannel(uint8 channel); + void disablePaulaChannel(uint8 channel); + void setupPaulaChannel(uint8 channel, const int8 *data, uint16 len, uint16 repeatPos, uint16 repeatLen); + + virtual void interrupt(); + + uint8 _ticks; + uint16 _delay; + SoundFxInstrument _instruments[NUM_INSTRUMENTS]; + uint8 _numOrders; + uint8 _curOrder; + uint16 _curPos; + uint8 _ordersTable[128]; + uint8 *_patternData; + int8 *_instrumentData; + int _eventsFreq; + uint16 _effects[NUM_CHANNELS]; +}; + +SoundFx::SoundFx(int rate, bool stereo) + : Paula(stereo, rate) { + _ticks = 0; + _delay = 0; + memset(_instruments, 0, sizeof(_instruments)); + _numOrders = 0; + _curOrder = 0; + _curPos = 0; + memset(_ordersTable, 0, sizeof(_ordersTable)); + _patternData = 0; + _instrumentData = 0; + _eventsFreq = 0; + memset(_effects, 0, sizeof(_effects)); +} + +SoundFx::~SoundFx() { + free(_patternData); + free(_instrumentData); +} + +bool SoundFx::load(Common::SeekableReadStream *data) { + bool loaded = false; + int instrumentsSize[15]; + int totalInstrumentsSize = 0; + for (int i = 0; i < NUM_INSTRUMENTS; ++i) { + instrumentsSize[i] = data->readUint32BE(); + totalInstrumentsSize += instrumentsSize[i]; + } + uint8 tag[4]; + data->read(tag, 4); + if (memcmp(tag, "SONG", 4) == 0) { + _delay = data->readUint16BE(); + data->skip(7 * 2); + for (int i = 0; i < NUM_INSTRUMENTS; ++i) { + SoundFxInstrument *ins = &_instruments[i]; + data->read(ins->name, 22); ins->name[22] = 0; + ins->len = data->readUint16BE(); + ins->finetune = data->readByte(); + ins->volume = data->readByte(); + ins->repeatPos = data->readUint16BE(); + ins->repeatLen = data->readUint16BE(); + } + _numOrders = data->readByte(); + data->skip(1); + data->read(_ordersTable, 128); + int maxOrder = 0; + for (int i = 0; i < _numOrders; ++i) { + if (_ordersTable[i] > maxOrder) { + maxOrder = _ordersTable[i]; + } + } + int patternSize = (maxOrder + 1) * 4 * 4 * 64; + _patternData = (uint8 *)malloc(patternSize); + if (_patternData) { + data->read(_patternData, patternSize); + _instrumentData = (int8 *)malloc(totalInstrumentsSize); + if (_instrumentData) { + data->read(_instrumentData, totalInstrumentsSize); + int8 *p = _instrumentData; + for (int i = 0; i < NUM_INSTRUMENTS; ++i) { + SoundFxInstrument *ins = &_instruments[i]; + assert(ins->len <= 2 || ins->len * 2 <= instrumentsSize[i]); + assert(ins->repeatLen <= 2 || (ins->repeatPos + ins->repeatLen) * 2 <= instrumentsSize[i]); + if (instrumentsSize[i] != 0) { + ins->data = p; + p += instrumentsSize[i]; + } + } + loaded = true; + } + } + } + return loaded; +} + +void SoundFx::play() { + _curPos = 0; + _curOrder = 0; + _ticks = 0; + _eventsFreq = CIA_FREQ / _delay; + setInterruptFreq(_rate / _eventsFreq); + startPaula(); +} + +void SoundFx::handlePattern(int ch, uint32 pat) { + uint16 note1 = pat >> 16; + uint16 note2 = pat & 0xFFFF; + if (note1 != 0xFFFD) { + int ins = (note2 & 0xF000) >> 12; + if (ins != 0) { + SoundFxInstrument *i = &_instruments[ins - 1]; + setupPaulaChannel(ch, i->data, i->len, i->repeatPos, i->repeatLen); + int effect = (note2 & 0xF00) >> 8; + int volume = i->volume; + switch (effect) { + case 5: // volume up + volume += (note2 & 0xFF); + if (volume > 63) { + volume = 63; + } + break; + case 6: // volume down + volume -= (note2 & 0xFF); + if (volume < 0) { + volume = 0; + } + break; + } + setPaulaChannelVolume(ch, volume); + } + } + _effects[ch] = note2; + if (note1 == 0xFFFD) { // PIC + _effects[ch] = 0; + } else if (note1 == 0xFFFE) { // STP + disablePaulaChannel(ch); + } else if (note1 != 0) { + setPaulaChannelPeriod(ch, note1); + enablePaulaChannel(ch); + } +} + +void SoundFx::updateEffects(int ch) { + // updateEffects() is a no-op in all Delphine Software games using SoundFx : FW,OS,Cruise,AW + if (_effects[ch] != 0) { + switch (_effects[ch]) { + case 1: // appreggiato + case 2: // pitchbend + case 3: // ledon, enable low-pass filter + case 4: // ledoff, disable low-pass filter + case 7: // set step up + case 8: // set step down + warning("Unhandled effect %d\n", _effects[ch]); + break; + } + } +} + +void SoundFx::handleTick() { + ++_ticks; + if (_ticks != 6) { + for (int ch = 0; ch < 4; ++ch) { + updateEffects(ch); + } + } else { + _ticks = 0; + const uint8 *patternData = _patternData + _ordersTable[_curOrder] * 1024 + _curPos; + for (int ch = 0; ch < 4; ++ch) { + handlePattern(ch, READ_BE_UINT32(patternData)); + patternData += 4; + } + _curPos += 4 * 4; + if (_curPos >= 1024) { + _curPos = 0; + ++_curOrder; + if (_curOrder == _numOrders) { + stopPaula(); + } + } + } +} + +void SoundFx::startPaula() { + _playing = true; + _end = false; +} + +void SoundFx::stopPaula() { + _playing = false; + _end = true; +} + +void SoundFx::setPaulaChannelPeriod(uint8 channel, int16 period) { + _voice[channel].period = period; +} + +void SoundFx::setPaulaChannelVolume(uint8 channel, uint8 volume) { + _voice[channel].volume = volume; +} + +void SoundFx::enablePaulaChannel(uint8 channel) { +} + +void SoundFx::disablePaulaChannel(uint8 channel) { + _voice[channel].period = 0; +} + +void SoundFx::setupPaulaChannel(uint8 channel, const int8 *data, uint16 len, uint16 repeatPos, uint16 repeatLen) { + if (data && len > 1) { + Channel *ch = &_voice[channel]; + ch->data = data; + ch->dataRepeat = data + repeatPos * 2; + ch->length = len * 2; + ch->lengthRepeat = repeatLen * 2; + ch->offset = 0; + } +} + +void SoundFx::interrupt() { + handleTick(); +} + +AudioStream *makeSoundFxStream(Common::SeekableReadStream *data, int rate, bool stereo) { + SoundFx *stream = new SoundFx(rate, stereo); + if (stream->load(data)) { + stream->play(); + return stream; + } + delete stream; + return 0; +} + +} // End of namespace Audio diff --git a/sound/mods/soundfx.h b/sound/mods/soundfx.h new file mode 100644 index 0000000000..c48b9a0c69 --- /dev/null +++ b/sound/mods/soundfx.h @@ -0,0 +1,37 @@ + +/* ScummVM - Scumm Interpreter + * Copyright (C) 2007 The ScummVM project + * + * 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 SOUND_MODS_SOUNDFX_H +#define SOUND_MODS_SOUNDFX_H + +#include "common/stream.h" + +namespace Audio { + +class AudioStream; + +AudioStream *makeSoundFxStream(Common::SeekableReadStream *data, int rate = 44100, bool stereo = true); + +} // End of namespace Audio + +#endif diff --git a/sound/module.mk b/sound/module.mk index becc4b4d1e..a204dbadb9 100644 --- a/sound/module.mk +++ b/sound/module.mk @@ -25,6 +25,7 @@ MODULE_OBJS := \ mods/protracker.o \ mods/paula.o \ mods/rjp1.o \ + mods/soundfx.o \ softsynth/adlib.o \ softsynth/ym2612.o \ softsynth/fluidsynth.o \ -- cgit v1.2.3