aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/sfx/softseq
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/sfx/softseq')
-rw-r--r--engines/sci/sfx/softseq/adlib.cpp135
-rw-r--r--engines/sci/sfx/softseq/adlib.h160
-rw-r--r--engines/sci/sfx/softseq/amiga.cpp525
3 files changed, 417 insertions, 403 deletions
diff --git a/engines/sci/sfx/softseq/adlib.cpp b/engines/sci/sfx/softseq/adlib.cpp
index e21ddf11f9..a361d33104 100644
--- a/engines/sci/sfx/softseq/adlib.cpp
+++ b/engines/sci/sfx/softseq/adlib.cpp
@@ -27,9 +27,10 @@
#include "sci/sfx/iterator.h"
#include "sound/fmopl.h"
+#include "sound/softsynth/emumidi.h"
#include "sci/resource.h"
-#include "sci/sfx/softseq/adlib.h"
+#include "sci/sfx/sci_midi.h"
namespace Sci {
@@ -42,6 +43,134 @@ namespace Sci {
// FIXME: We don't seem to be sending the polyphony init data, so disable this for now
#define ADLIB_DISABLE_VOICE_MAPPING
+class MidiDriver_Adlib : public MidiDriver_Emulated {
+public:
+ enum {
+ kVoices = 9,
+ kRhythmKeys = 62
+ };
+
+ MidiDriver_Adlib(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer), _playSwitch(true), _masterVolume(15), _rhythmKeyMap(0), _opl(0) { }
+ virtual ~MidiDriver_Adlib() { }
+
+ // MidiDriver
+ int open(bool isSCI0);
+ void close();
+ void send(uint32 b);
+ MidiChannel *allocateChannel() { return NULL; }
+ MidiChannel *getPercussionChannel() { return NULL; }
+
+ // AudioStream
+ bool isStereo() const { return _stereo; }
+ int getRate() const { return _mixer->getOutputRate(); }
+
+ // MidiDriver_Emulated
+ void generateSamples(int16 *buf, int len);
+
+ void setVolume(byte volume);
+ void playSwitch(bool play);
+ bool loadResource(const byte *data, uint size);
+ virtual uint32 property(int prop, uint32 param);
+
+private:
+ enum ChannelID {
+ kLeftChannel = 1,
+ kRightChannel = 2
+ };
+
+ struct AdlibOperator {
+ bool amplitudeMod;
+ bool vibrato;
+ bool envelopeType;
+ bool kbScaleRate;
+ byte frequencyMult; // (0-15)
+ byte kbScaleLevel; // (0-3)
+ byte totalLevel; // (0-63, 0=max, 63=min)
+ byte attackRate; // (0-15)
+ byte decayRate; // (0-15)
+ byte sustainLevel; // (0-15)
+ byte releaseRate; // (0-15)
+ byte waveForm; // (0-3)
+ };
+
+ struct AdlibModulator {
+ byte feedback; // (0-7)
+ bool algorithm;
+ };
+
+ struct AdlibPatch {
+ AdlibOperator op[2];
+ AdlibModulator mod;
+ };
+
+ struct Channel {
+ uint8 patch; // Patch setting
+ uint8 volume; // Channel volume (0-63)
+ uint8 pan; // Pan setting (0-127, 64 is center)
+ uint8 holdPedal; // Hold pedal setting (0 to 63 is off, 127 to 64 is on)
+ uint8 extraVoices; // The number of additional voices this channel optimally needs
+ uint16 pitchWheel; // Pitch wheel setting (0-16383, 8192 is center)
+ uint8 lastVoice; // Last voice used for this MIDI channel
+ bool enableVelocity; // Enable velocity control (SCI0)
+
+ Channel() : patch(0), volume(63), pan(64), holdPedal(0), extraVoices(0),
+ pitchWheel(8192), lastVoice(0), enableVelocity(false) { }
+ };
+
+ struct AdlibVoice {
+ int8 channel; // MIDI channel that this voice is assigned to or -1
+ int8 note; // Currently playing MIDI note or -1
+ int patch; // Currently playing patch or -1
+ uint8 velocity; // Note velocity
+ bool isSustained; // Flag indicating a note that is being sustained by the hold pedal
+ uint16 age; // Age of the current note
+
+ AdlibVoice() : channel(-1), note(-1), patch(-1), velocity(0), isSustained(false), age(0) { }
+ };
+
+ bool _stereo;
+ bool _isSCI0;
+ OPL::OPL *_opl;
+ bool _playSwitch;
+ int _masterVolume;
+ Channel _channels[MIDI_CHANNELS];
+ AdlibVoice _voices[kVoices];
+ byte *_rhythmKeyMap;
+ Common::Array<AdlibPatch> _patches;
+
+ void loadInstrument(const byte *ins);
+ void voiceOn(int voice, int note, int velocity);
+ void voiceOff(int voice);
+ void setPatch(int voice, int patch);
+ void setNote(int voice, int note, bool key);
+ void setVelocity(int voice);
+ void setOperator(int oper, AdlibOperator &op);
+ void setRegister(int reg, int value, int channels = kLeftChannel | kRightChannel);
+ void renewNotes(int channel, bool key);
+ void noteOn(int channel, int note, int velocity);
+ void noteOff(int channel, int note);
+ int findVoice(int channel);
+ void voiceMapping(int channel, int voices);
+ void assignVoices(int channel, int voices);
+ void releaseVoices(int channel, int voices);
+ void donateVoices();
+ int findVoiceBasic(int channel);
+ void setVelocityReg(int regOffset, int velocity, int kbScaleLevel, int pan);
+ int calcVelocity(int voice, int op);
+};
+
+class MidiPlayer_Adlib : public MidiPlayer {
+public:
+ MidiPlayer_Adlib() { _driver = new MidiDriver_Adlib(g_system->getMixer()); }
+ int open(ResourceManager *resMan);
+ int getPlayMask() const { return 0x04; }
+ int getPolyphony() const { return MidiDriver_Adlib::kVoices; }
+ bool hasRhythmChannel() const { return false; }
+ void setVolume(byte volume) { static_cast<MidiDriver_Adlib *>(_driver)->setVolume(volume); }
+ void playSwitch(bool play) { static_cast<MidiDriver_Adlib *>(_driver)->playSwitch(play); }
+ void loadInstrument(int idx, byte *data);
+};
+
static const byte registerOffset[MidiDriver_Adlib::kVoices] = {
0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12
};
@@ -679,4 +808,8 @@ int MidiPlayer_Adlib::open(ResourceManager *resMan) {
return static_cast<MidiDriver_Adlib *>(_driver)->open(getSciVersion() <= SCI_VERSION_0_LATE);
}
+MidiPlayer *MidiPlayer_Adlib_create() {
+ return new MidiPlayer_Adlib();
+}
+
} // End of namespace Sci
diff --git a/engines/sci/sfx/softseq/adlib.h b/engines/sci/sfx/softseq/adlib.h
deleted file mode 100644
index 63dd9021a1..0000000000
--- a/engines/sci/sfx/softseq/adlib.h
+++ /dev/null
@@ -1,160 +0,0 @@
-/* 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$
- *
- */
-
-#include "sci/sfx/sci_midi.h"
-#include "sound/fmopl.h"
-
-namespace Sci {
-
-class MidiDriver_Adlib : public MidiDriver_Emulated {
-public:
- enum {
- kVoices = 9,
- kRhythmKeys = 62
- };
-
- MidiDriver_Adlib(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer), _playSwitch(true), _masterVolume(15), _rhythmKeyMap(0), _opl(0) { }
- virtual ~MidiDriver_Adlib() { }
-
- // MidiDriver
- int open(bool isSCI0);
- void close();
- void send(uint32 b);
- MidiChannel *allocateChannel() { return NULL; }
- MidiChannel *getPercussionChannel() { return NULL; }
-
- // AudioStream
- bool isStereo() const { return _stereo; }
- int getRate() const { return _mixer->getOutputRate(); }
-
- // MidiDriver_Emulated
- void generateSamples(int16 *buf, int len);
-
- void setVolume(byte volume);
- void playSwitch(bool play);
- bool loadResource(const byte *data, uint size);
- uint32 property(int prop, uint32 param);
-
-private:
- enum ChannelID {
- kLeftChannel = 1,
- kRightChannel = 2
- };
-
- struct AdlibOperator {
- bool amplitudeMod;
- bool vibrato;
- bool envelopeType;
- bool kbScaleRate;
- byte frequencyMult; // (0-15)
- byte kbScaleLevel; // (0-3)
- byte totalLevel; // (0-63, 0=max, 63=min)
- byte attackRate; // (0-15)
- byte decayRate; // (0-15)
- byte sustainLevel; // (0-15)
- byte releaseRate; // (0-15)
- byte waveForm; // (0-3)
- };
-
- struct AdlibModulator {
- byte feedback; // (0-7)
- bool algorithm;
- };
-
- struct AdlibPatch {
- AdlibOperator op[2];
- AdlibModulator mod;
- };
-
- struct Channel {
- uint8 patch; // Patch setting
- uint8 volume; // Channel volume (0-63)
- uint8 pan; // Pan setting (0-127, 64 is center)
- uint8 holdPedal; // Hold pedal setting (0 to 63 is off, 127 to 64 is on)
- uint8 extraVoices; // The number of additional voices this channel optimally needs
- uint16 pitchWheel; // Pitch wheel setting (0-16383, 8192 is center)
- uint8 lastVoice; // Last voice used for this MIDI channel
- bool enableVelocity; // Enable velocity control (SCI0)
-
- Channel() : patch(0), volume(63), pan(64), holdPedal(0), extraVoices(0),
- pitchWheel(8192), lastVoice(0), enableVelocity(false) { }
- };
-
- struct AdlibVoice {
- int8 channel; // MIDI channel that this voice is assigned to or -1
- int8 note; // Currently playing MIDI note or -1
- int patch; // Currently playing patch or -1
- uint8 velocity; // Note velocity
- bool isSustained; // Flag indicating a note that is being sustained by the hold pedal
- uint16 age; // Age of the current note
-
- AdlibVoice() : channel(-1), note(-1), patch(-1), velocity(0), isSustained(false), age(0) { }
- };
-
- bool _stereo;
- bool _isSCI0;
- OPL::OPL *_opl;
- bool _playSwitch;
- int _masterVolume;
- Channel _channels[MIDI_CHANNELS];
- AdlibVoice _voices[kVoices];
- byte *_rhythmKeyMap;
- Common::Array<AdlibPatch> _patches;
-
- void loadInstrument(const byte *ins);
- void voiceOn(int voice, int note, int velocity);
- void voiceOff(int voice);
- void setPatch(int voice, int patch);
- void setNote(int voice, int note, bool key);
- void setVelocity(int voice);
- void setOperator(int oper, AdlibOperator &op);
- void setRegister(int reg, int value, int channels = kLeftChannel | kRightChannel);
- void renewNotes(int channel, bool key);
- void noteOn(int channel, int note, int velocity);
- void noteOff(int channel, int note);
- int findVoice(int channel);
- void voiceMapping(int channel, int voices);
- void assignVoices(int channel, int voices);
- void releaseVoices(int channel, int voices);
- void donateVoices();
- int findVoiceBasic(int channel);
- void setVelocityReg(int regOffset, int velocity, int kbScaleLevel, int pan);
- int calcVelocity(int voice, int op);
-};
-
-class MidiPlayer_Adlib : public MidiPlayer {
-public:
- MidiPlayer_Adlib() { _driver = new MidiDriver_Adlib(g_system->getMixer()); }
- int open(ResourceManager *resMan);
- int getPlayMask() const { return 0x04; }
- int getPolyphony() const { return MidiDriver_Adlib::kVoices; }
- bool hasRhythmChannel() const { return false; }
- void setVolume(byte volume) { static_cast<MidiDriver_Adlib *>(_driver)->setVolume(volume); }
- void playSwitch(bool play) { static_cast<MidiDriver_Adlib *>(_driver)->playSwitch(play); }
- void loadInstrument(int idx, byte *data);
-};
-
-} // End of namespace Sci
-
diff --git a/engines/sci/sfx/softseq/amiga.cpp b/engines/sci/sfx/softseq/amiga.cpp
index 388e5685fd..1910e3b420 100644
--- a/engines/sci/sfx/softseq/amiga.cpp
+++ b/engines/sci/sfx/softseq/amiga.cpp
@@ -25,93 +25,19 @@
#include "sci/sfx/softseq.h"
+#include "sound/softsynth/emumidi.h"
+#include "sci/sfx/sci_midi.h"
+
#include "common/file.h"
#include "common/frac.h"
#include "common/util.h"
namespace Sci {
-#define FREQUENCY 44100
-#define CHANNELS_NR 10
-#define HW_CHANNELS_NR 16
-
-/* Samplerate of the instrument bank */
-#define BASE_FREQ 20000
-
-/* Instrument looping flag */
-#define MODE_LOOP 1 << 0
-/* Instrument pitch changes flag */
-#define MODE_PITCH 1 << 1
-
-#define PAN_LEFT 91
-#define PAN_RIGHT 164
-
/* #define DEBUG */
-struct envelope_t {
- /* Phase period length in samples */
- int length;
- /* Velocity delta per period */
- int delta;
- /* Target velocity */
- int target;
-};
-
-/* Fast decay envelope */
-static envelope_t env_decay = {FREQUENCY / (32 * 64), 1, 0};
-
-struct instrument_t {
- char name[30];
- int mode;
- /* Size of non-looping part in bytes */
- int size;
- /* Starting offset and size of loop in bytes */
- int loop_size;
- /* Transpose value in semitones */
- int transpose;
- /* Envelope */
- envelope_t envelope[4];
- int8 *samples;
- int8 *loop;
-};
-
-struct bank_t {
- char name[30];
- int size;
- instrument_t *instruments[256];
-};
-
-struct channel_t {
- int instrument;
- int note;
- int note_velocity;
- int velocity;
- int envelope;
- /* Number of samples till next envelope event */
- int envelope_samples;
- int decay;
- int looping;
- int hw_channel;
- frac_t offset;
- frac_t rate;
-};
-
-struct hw_channel_t {
- int instrument;
- int volume;
- int pan;
-};
-
-/* Instrument bank */
-static bank_t bank;
-/* Internal channels */
-static channel_t channels[CHANNELS_NR];
-/* External channels */
-static hw_channel_t hw_channels[HW_CHANNELS_NR];
-/* Overall volume */
-static int volume = 127;
-
-/* Frequencies for every note */
+// Frequencies for every note
+// FIXME Store only one octave
static const int freq_table[] = {
58, 62, 65, 69, 73, 78, 82, 87,
92, 98, 104, 110, 117, 124, 131, 139,
@@ -131,7 +57,110 @@ static const int freq_table[] = {
59932, 63496, 67271, 71271, 75509, 80000, 84757, 89796
};
-static void set_envelope(channel_t *channel, envelope_t *envelope, int phase) {
+class MidiDriver_Amiga : public MidiDriver_Emulated {
+public:
+ enum {
+ kVoices = 4
+ };
+
+ MidiDriver_Amiga(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer), _playSwitch(true), _masterVolume(15) { }
+ virtual ~MidiDriver_Amiga() { }
+
+ // MidiDriver
+ int open();
+ void close();
+ void send(uint32 b);
+ MidiChannel *allocateChannel() { return NULL; }
+ MidiChannel *getPercussionChannel() { return NULL; }
+
+ // AudioStream
+ bool isStereo() const { return true; }
+ int getRate() const { return _mixer->getOutputRate(); }
+
+ // MidiDriver_Emulated
+ void generateSamples(int16 *buf, int len);
+
+ void setVolume(byte volume);
+ void playSwitch(bool play);
+ virtual uint32 property(int prop, uint32 param);
+
+private:
+ enum {
+ kModeLoop = 1 << 0, // Instrument looping flag
+ kModePitch = 1 << 1 // Instrument pitch changes flag
+ };
+
+ enum {
+ kChannels = 10,
+ kBaseFreq = 20000, // Samplerate of the instrument bank
+ kPanLeft = 91,
+ kPanRight = 164
+ };
+
+ struct Channel {
+ int instrument;
+ int volume;
+ int pan;
+ };
+
+ struct Envelope {
+ int length; // Phase period length in samples
+ int delta; // Velocity delta per period
+ int target; // Target velocity
+ };
+
+ struct Voice {
+ int instrument;
+ int note;
+ int note_velocity;
+ int velocity;
+ int envelope;
+ int envelope_samples; // Number of samples till next envelope event
+ int decay;
+ int looping;
+ int hw_channel;
+ frac_t offset;
+ frac_t rate;
+ };
+
+ struct Instrument {
+ char name[30];
+ int mode;
+ int size; // Size of non-looping part in bytes
+ int loop_size; // Starting offset and size of loop in bytes
+ int transpose; // Transpose value in semitones
+ Envelope envelope[4]; // Envelope
+ int8 *samples;
+ int8 *loop;
+ };
+
+ struct Bank {
+ char name[30];
+ uint size;
+ Instrument *instruments[256];
+ };
+
+ bool _playSwitch;
+ int _masterVolume;
+ int _frequency;
+ Envelope _envDecay;
+ Bank _bank; // Instrument bank
+
+ Channel _channels[MIDI_CHANNELS];
+ /* Internal channels */
+ Voice _voices[kChannels];
+
+ void setEnvelope(Voice *channel, Envelope *envelope, int phase);
+ int interpolate(int8 *samples, frac_t offset);
+ void playInstrument(int16 *dest, Voice *channel, int count);
+ void changeInstrument(int channel, int instrument);
+ void stopChannel(int ch);
+ void stopNote(int ch, int note);
+ void startNote(int ch, int note, int velocity);
+ Instrument *readInstrument(Common::File &file, int *id);
+};
+
+void MidiDriver_Amiga::setEnvelope(Voice *channel, Envelope *envelope, int phase) {
channel->envelope = phase;
channel->envelope_samples = envelope[phase].length;
@@ -141,17 +170,17 @@ static void set_envelope(channel_t *channel, envelope_t *envelope, int phase) {
channel->velocity = envelope[phase - 1].target;
}
-static inline int interpolate(int8 *samples, frac_t offset) {
+int MidiDriver_Amiga::interpolate(int8 *samples, frac_t offset) {
int x = fracToInt(offset);
int diff = (samples[x + 1] - samples[x]) << 8;
return (samples[x] << 8) + fracToInt(diff * (offset & FRAC_LO_MASK));
}
-static void play_instrument(int16 *dest, channel_t *channel, int count) {
+void MidiDriver_Amiga::playInstrument(int16 *dest, Voice *channel, int count) {
int index = 0;
- int vol = hw_channels[channel->hw_channel].volume;
- instrument_t *instrument = bank.instruments[channel->instrument];
+ int vol = _channels[channel->hw_channel].volume;
+ Instrument *instrument = _bank.instruments[channel->instrument];
while (1) {
/* Available source samples until end of segment */
@@ -193,11 +222,11 @@ static void play_instrument(int16 *dest, channel_t *channel, int count) {
channel->envelope_samples -= amount;
if (channel->envelope_samples == 0) {
- envelope_t *envelope;
+ Envelope *envelope;
int delta, target, velocity;
if (channel->decay)
- envelope = &env_decay;
+ envelope = &_envDecay;
else
envelope = &instrument->envelope[channel->envelope];
@@ -218,7 +247,7 @@ static void play_instrument(int16 *dest, channel_t *channel, int count) {
case 0:
case 2:
/* Go to next phase */
- set_envelope(channel, instrument->envelope, channel->envelope + 1);
+ setEnvelope(channel, instrument->envelope, channel->envelope + 1);
break;
case 1:
case 3:
@@ -237,7 +266,7 @@ static void play_instrument(int16 *dest, channel_t *channel, int count) {
break;
if (fracToInt(channel->offset) >= seg_end) {
- if (instrument->mode & MODE_LOOP) {
+ if (instrument->mode & kModeLoop) {
/* Loop the samples */
channel->offset -= intToFrac(seg_end);
channel->looping = 1;
@@ -250,79 +279,79 @@ static void play_instrument(int16 *dest, channel_t *channel, int count) {
}
}
-static void change_instrument(int channel, int instrument) {
+void MidiDriver_Amiga::changeInstrument(int channel, int instrument) {
#ifdef DEBUG
- if (bank.instruments[instrument])
- printf("[sfx:seq:amiga] Setting channel %i to \"%s\" (%i)\n", channel, bank.instruments[instrument]->name, instrument);
+ if (_bank.instruments[instrument])
+ printf("[sfx:seq:amiga] Setting channel %i to \"%s\" (%i)\n", channel, _bank.instruments[instrument]->name, instrument);
else
warning("[sfx:seq:amiga] instrument %i does not exist (channel %i)", instrument, channel);
#endif
- hw_channels[channel].instrument = instrument;
+ _channels[channel].instrument = instrument;
}
-static void stop_channel(int ch) {
+void MidiDriver_Amiga::stopChannel(int ch) {
int i;
/* Start decay phase for note on this hw channel, if any */
- for (i = 0; i < CHANNELS_NR; i++)
- if (channels[i].note != -1 && channels[i].hw_channel == ch && !channels[i].decay) {
+ for (i = 0; i < kChannels; i++)
+ if (_voices[i].note != -1 && _voices[i].hw_channel == ch && !_voices[i].decay) {
/* Trigger fast decay envelope */
- channels[i].decay = 1;
- channels[i].envelope_samples = env_decay.length;
+ _voices[i].decay = 1;
+ _voices[i].envelope_samples = _envDecay.length;
break;
}
}
-static void stop_note(int ch, int note) {
+void MidiDriver_Amiga::stopNote(int ch, int note) {
int channel;
- instrument_t *instrument;
+ Instrument *instrument;
- for (channel = 0; channel < CHANNELS_NR; channel++)
- if (channels[channel].note == note && channels[channel].hw_channel == ch && !channels[channel].decay)
+ for (channel = 0; channel < kChannels; channel++)
+ if (_voices[channel].note == note && _voices[channel].hw_channel == ch && !_voices[channel].decay)
break;
- if (channel == CHANNELS_NR) {
+ if (channel == kChannels) {
#ifdef DEBUG
warning("[sfx:seq:amiga] cannot stop note %i on channel %i", note, ch);
#endif
return;
}
- instrument = bank.instruments[channels[channel].instrument];
+ instrument = _bank.instruments[_voices[channel].instrument];
/* Start the envelope phases for note-off if looping is on and envelope is enabled */
- if ((instrument->mode & MODE_LOOP) && (instrument->envelope[0].length != 0))
- set_envelope(&channels[channel], instrument->envelope, 2);
+ if ((instrument->mode & kModeLoop) && (instrument->envelope[0].length != 0))
+ setEnvelope(&_voices[channel], instrument->envelope, 2);
}
-static void start_note(int ch, int note, int velocity) {
- instrument_t *instrument;
+void MidiDriver_Amiga::startNote(int ch, int note, int velocity) {
+ Instrument *instrument;
int channel;
- if (hw_channels[ch].instrument < 0 || hw_channels[ch].instrument > 255) {
- warning("[sfx:seq:amiga] invalid instrument %i on channel %i", hw_channels[ch].instrument, ch);
+ if (_channels[ch].instrument < 0 || _channels[ch].instrument > 255) {
+ warning("[sfx:seq:amiga] invalid instrument %i on channel %i", _channels[ch].instrument, ch);
return;
}
- instrument = bank.instruments[hw_channels[ch].instrument];
+ instrument = _bank.instruments[_channels[ch].instrument];
if (!instrument) {
- warning("[sfx:seq:amiga] instrument %i does not exist", hw_channels[ch].instrument);
+ warning("[sfx:seq:amiga] instrument %i does not exist", _channels[ch].instrument);
return;
}
- for (channel = 0; channel < CHANNELS_NR; channel++)
- if (channels[channel].note == -1)
+ for (channel = 0; channel < kChannels; channel++)
+ if (_voices[channel].note == -1)
break;
- if (channel == CHANNELS_NR) {
+ if (channel == kChannels) {
warning("[sfx:seq:amiga] could not find a free channel");
return;
}
- stop_channel(ch);
+ stopChannel(ch);
- if (instrument->mode & MODE_PITCH) {
+ if (instrument->mode & kModePitch) {
int fnote = note + instrument->transpose;
if (fnote < 0 || fnote > 127) {
@@ -331,40 +360,32 @@ static void start_note(int ch, int note, int velocity) {
}
/* Compute rate for note */
- channels[channel].rate = doubleToFrac(freq_table[fnote] / (double) FREQUENCY);
+ _voices[channel].rate = doubleToFrac(freq_table[fnote] / (double) _frequency);
} else
- channels[channel].rate = doubleToFrac(BASE_FREQ / (double) FREQUENCY);
+ _voices[channel].rate = doubleToFrac(kBaseFreq / (double) _frequency);
- channels[channel].instrument = hw_channels[ch].instrument;
- channels[channel].note = note;
- channels[channel].note_velocity = velocity;
+ _voices[channel].instrument = _channels[ch].instrument;
+ _voices[channel].note = note;
+ _voices[channel].note_velocity = velocity;
- if ((instrument->mode & MODE_LOOP) && (instrument->envelope[0].length != 0))
- set_envelope(&channels[channel], instrument->envelope, 0);
+ if ((instrument->mode & kModeLoop) && (instrument->envelope[0].length != 0))
+ setEnvelope(&_voices[channel], instrument->envelope, 0);
else {
- channels[channel].velocity = 64;
- channels[channel].envelope_samples = -1;
+ _voices[channel].velocity = 64;
+ _voices[channel].envelope_samples = -1;
}
- channels[channel].offset = 0;
- channels[channel].hw_channel = ch;
- channels[channel].decay = 0;
- channels[channel].looping = 0;
-}
-
-static int16 read_int16(byte *data) {
- return (data[0] << 8) | data[1];
+ _voices[channel].offset = 0;
+ _voices[channel].hw_channel = ch;
+ _voices[channel].decay = 0;
+ _voices[channel].looping = 0;
}
-static int32 read_int32(byte *data) {
- return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
-}
-
-static instrument_t *read_instrument(Common::File &file, int *id) {
- instrument_t *instrument;
+MidiDriver_Amiga::Instrument *MidiDriver_Amiga::readInstrument(Common::File &file, int *id) {
+ Instrument *instrument;
byte header[61];
int size;
- int seg_size[3];
+ int16 seg_size[3];
int loop_offset;
int i;
@@ -373,11 +394,11 @@ static instrument_t *read_instrument(Common::File &file, int *id) {
return NULL;
}
- instrument = (instrument_t *) malloc(sizeof(instrument_t));
+ instrument = new Instrument;
- seg_size[0] = read_int16(header + 35) * 2;
- seg_size[1] = read_int16(header + 41) * 2;
- seg_size[2] = read_int16(header + 47) * 2;
+ seg_size[0] = READ_BE_UINT16(header + 35) * 2;
+ seg_size[1] = READ_BE_UINT16(header + 41) * 2;
+ seg_size[2] = READ_BE_UINT16(header + 47) * 2;
instrument->mode = header[33];
instrument->transpose = (int8) header[34];
@@ -387,17 +408,17 @@ static instrument_t *read_instrument(Common::File &file, int *id) {
if (length == 0 && i > 0)
length = 256;
- instrument->envelope[i].length = length * FREQUENCY / 60;
- instrument->envelope[i].delta = (int8) header[53 + i];
+ instrument->envelope[i].length = length * _frequency / 60;
+ instrument->envelope[i].delta = (int8)header[53 + i];
instrument->envelope[i].target = header[57 + i];
}
/* Final target must be 0 */
instrument->envelope[3].target = 0;
- loop_offset = read_int32(header + 37) & ~1;
+ loop_offset = READ_BE_UINT32(header + 37) & ~1;
size = seg_size[0] + seg_size[1] + seg_size[2];
- *id = read_int16(header);
+ *id = READ_BE_UINT16(header);
strncpy(instrument->name, (char *) header + 2, 29);
instrument->name[29] = 0;
@@ -405,8 +426,8 @@ static instrument_t *read_instrument(Common::File &file, int *id) {
printf("[sfx:seq:amiga] Reading instrument %i: \"%s\" (%i bytes)\n",
*id, instrument->name, size);
printf(" Mode: %02x\n", instrument->mode);
- printf(" Looping: %s\n", instrument->mode & MODE_LOOP ? "on" : "off");
- printf(" Pitch changes: %s\n", instrument->mode & MODE_PITCH ? "on" : "off");
+ printf(" Looping: %s\n", instrument->mode & kModeLoop ? "on" : "off");
+ printf(" Pitch changes: %s\n", instrument->mode & kModePitch ? "on" : "off");
printf(" Segment sizes: %i %i %i\n", seg_size[0], seg_size[1], seg_size[2]);
printf(" Segment offsets: 0 %i %i\n", loop_offset, read_int32(header + 43));
#endif
@@ -418,7 +439,7 @@ static instrument_t *read_instrument(Common::File &file, int *id) {
return NULL;
}
- if (instrument->mode & MODE_LOOP) {
+ if (instrument->mode & kModeLoop) {
if (loop_offset + seg_size[1] > size) {
#ifdef DEBUG
warning("[sfx:seq:amiga] looping samples extend %i bytes past end of sample block",
@@ -443,6 +464,7 @@ static instrument_t *read_instrument(Common::File &file, int *id) {
instrument->samples[instrument->size] = instrument->loop[0];
instrument->loop[instrument->loop_size] = instrument->loop[0];
} else {
+ instrument->loop = NULL;
instrument->size = size;
instrument->samples[instrument->size] = 0;
}
@@ -450,14 +472,26 @@ static instrument_t *read_instrument(Common::File &file, int *id) {
return instrument;
}
-static Common::Error ami_set_option(sfx_softseq_t *self, const char *name, const char *value) {
- return Common::kUnknownError;
+uint32 MidiDriver_Amiga::property(int prop, uint32 param) {
+ switch(prop) {
+ case MIDI_PROP_MASTER_VOLUME:
+ if (param != 0xffff)
+ _masterVolume = param;
+ return _masterVolume;
+ default:
+ break;
+ }
+ return 0;
}
-static Common::Error ami_init(sfx_softseq_t *self, byte *patch, int patch_len, byte *patch2, int patch2_len) {
+int MidiDriver_Amiga::open() {
+ _frequency = _mixer->getOutputRate();
+ _envDecay.length = _frequency / (32 * 64);
+ _envDecay.delta = 1;
+ _envDecay.target = 0;
+
Common::File file;
byte header[40];
- int i;
if (!file.open("bank.001")) {
warning("[sfx:seq:amiga] file bank.001 not found");
@@ -469,29 +503,29 @@ static Common::Error ami_init(sfx_softseq_t *self, byte *patch, int patch_len, b
return Common::kUnknownError;
}
- for (i = 0; i < 256; i++)
- bank.instruments[i] = NULL;
+ for (uint i = 0; i < 256; i++)
+ _bank.instruments[i] = NULL;
- for (i = 0; i < CHANNELS_NR; i++) {
- channels[i].note = -1;
+ for (uint i = 0; i < kChannels; i++) {
+ _voices[i].note = -1;
}
- for (i = 0; i < HW_CHANNELS_NR; i++) {
- hw_channels[i].instrument = -1;
- hw_channels[i].volume = 127;
- hw_channels[i].pan = (i % 4 == 0 || i % 4 == 3 ? PAN_LEFT : PAN_RIGHT);
+ for (uint i = 0; i < MIDI_CHANNELS; i++) {
+ _channels[i].instrument = -1;
+ _channels[i].volume = 127;
+ _channels[i].pan = (i % 4 == 0 || i % 4 == 3 ? kPanLeft : kPanRight);
}
- bank.size = read_int16(header + 38);
- strncpy(bank.name, (char *) header + 8, 29);
- bank.name[29] = 0;
+ _bank.size = READ_BE_UINT16(header + 38);
+ strncpy(_bank.name, (char *) header + 8, 29);
+ _bank.name[29] = 0;
#ifdef DEBUG
- printf("[sfx:seq:amiga] Reading %i instruments from bank \"%s\"\n", bank.size, bank.name);
+ printf("[sfx:seq:amiga] Reading %i instruments from bank \"%s\"\n", _bank.size, _bank.name);
#endif
- for (i = 0; i < bank.size; i++) {
+ for (uint i = 0; i < _bank.size; i++) {
int id;
- instrument_t *instrument = read_instrument(file, &id);
+ Instrument *instrument = readInstrument(file, &id);
if (!instrument) {
warning("[sfx:seq:amiga] failed to read bank.001");
@@ -503,124 +537,131 @@ static Common::Error ami_init(sfx_softseq_t *self, byte *patch, int patch_len, b
return Common::kUnknownError;
}
- bank.instruments[id] = instrument;
+ _bank.instruments[id] = instrument;
}
+ MidiDriver_Emulated::open();
+
+ _mixer->playInputStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, false);
+
return Common::kNoError;
}
-static void ami_exit(sfx_softseq_t *self) {
- int i;
+void MidiDriver_Amiga::close() {
+ _mixer->stopHandle(_mixerSoundHandle);
- for (i = 0; i < bank.size; i++) {
- if (bank.instruments[i]) {
- free(bank.instruments[i]->samples);
- free(bank.instruments[i]);
+ for (uint i = 0; i < _bank.size; i++) {
+ if (_bank.instruments[i]) {
+ if (_bank.instruments[i]->loop)
+ free(_bank.instruments[i]->loop);
+ free(_bank.instruments[i]->samples);
+ delete _bank.instruments[i];
}
}
}
-static void ami_event(sfx_softseq_t *self, byte command, int argc, byte *argv) {
- int channel, oper;
+void MidiDriver_Amiga::playSwitch(bool play) {
+ _playSwitch = play;
+}
- channel = command & 0x0f;
- oper = command & 0xf0;
+void MidiDriver_Amiga::setVolume(byte volume_) {
+ _masterVolume = volume_;
+}
- if (channel >= HW_CHANNELS_NR) {
-#ifdef DEBUG
- warning("[sfx:seq:amiga] received event for non-existing channel %i", channel);
-#endif
- return;
- }
+void MidiDriver_Amiga::send(uint32 b) {
+ byte command = b & 0xf0;
+ byte channel = b & 0xf;
+ byte op1 = (b >> 8) & 0xff;
+ byte op2 = (b >> 16) & 0xff;
- switch (oper) {
+ switch (command) {
case 0x90:
- if (argv[1] > 0)
- start_note(channel, argv[0], argv[1]);
+ if (op2 > 0)
+ startNote(channel, op1, op2);
else
- stop_note(channel, argv[0]);
+ stopNote(channel, op1);
break;
case 0xb0:
- switch (argv[0]) {
+ switch (op1) {
case 0x07:
- hw_channels[channel].volume = argv[1];
+ _channels[channel].volume = op2;
break;
case 0x0a:
#ifdef DEBUG
- warning("[sfx:seq:amiga] ignoring pan 0x%02x event for channel %i", argv[1], channel);
+ warning("[sfx:seq:amiga] ignoring pan 0x%02x event for channel %i", op2, channel);
#endif
break;
case 0x7b:
- stop_channel(channel);
+ stopChannel(channel);
break;
default:
- warning("[sfx:seq:amiga] unknown control event 0x%02x", argv[0]);
+ warning("[sfx:seq:amiga] unknown control event 0x%02x", op1);
}
break;
case 0xc0:
- change_instrument(channel, argv[0]);
+ changeInstrument(channel, op1);
break;
default:
warning("[sfx:seq:amiga] unknown event %02x", command);
}
}
-void ami_poll(sfx_softseq_t *self, byte *dest, int len) {
- int i, j;
- int16 *buf = (int16 *) dest;
- // FIXME: memleak
- int16 *buffers = (int16*)malloc(len * 2 * CHANNELS_NR);
+void MidiDriver_Amiga::generateSamples(int16 *data, int len) {
+ if (len == 0)
+ return;
- memset(buffers, 0, len * 2 * CHANNELS_NR);
- memset(dest, 0, len * 4);
+ int16 *buffers = (int16*)malloc(len * 2 * kChannels);
- /* Generate samples for all notes */
- for (i = 0; i < CHANNELS_NR; i++)
- if (channels[i].note >= 0)
- play_instrument(buffers + i * len, &channels[i], len);
+ memset(buffers, 0, len * 2 * kChannels);
- for (j = 0; j < len; j++) {
- int mixedl = 0, mixedr = 0;
+ /* Generate samples for all notes */
+ for (int i = 0; i < kChannels; i++)
+ if (_voices[i].note >= 0)
+ playInstrument(buffers + i * len, &_voices[i], len);
+
+ if (isStereo()) {
+ for (int j = 0; j < len; j++) {
+ int mixedl = 0, mixedr = 0;
+
+ /* Mix and pan */
+ for (int i = 0; i < kChannels; i++) {
+ mixedl += buffers[i * len + j] * (256 - _channels[_voices[i].hw_channel].pan);
+ mixedr += buffers[i * len + j] * _channels[_voices[i].hw_channel].pan;
+ }
- /* Mix and pan */
- for (i = 0; i < CHANNELS_NR; i++) {
- mixedl += buffers[i * len + j] * (256 - hw_channels[channels[i].hw_channel].pan);
- mixedr += buffers[i * len + j] * hw_channels[channels[i].hw_channel].pan;
+ /* Adjust volume */
+ data[2 * j] = mixedl * _masterVolume >> 13;
+ data[2 * j + 1] = mixedr * _masterVolume >> 13;
}
+ } else {
+ for (int j = 0; j < len; j++) {
+ int mixed = 0;
- /* Adjust volume */
- buf[2 * j] = mixedl * volume >> 16;
- buf[2 * j + 1] = mixedr * volume >> 16;
- }
-}
+ /* Mix */
+ for (int i = 0; i < kChannels; i++)
+ mixed += buffers[i * len + j];
-void ami_volume(sfx_softseq_t *self, int new_volume) {
- volume = new_volume;
-}
+ /* Adjust volume */
+ data[j] = mixed * _masterVolume >> 6;
+ }
+ }
-void ami_allstop(sfx_softseq_t *self) {
- int i;
- for (i = 0; i < HW_CHANNELS_NR; i++)
- stop_channel(i);
+ free(buffers);
}
-sfx_softseq_t sfx_softseq_amiga = {
- "amiga",
- "0.1",
- ami_set_option,
- ami_init,
- ami_exit,
- ami_volume,
- ami_event,
- ami_poll,
- ami_allstop,
- NULL,
- SFX_SEQ_PATCHFILE_NONE,
- SFX_SEQ_PATCHFILE_NONE,
- 0x40,
- 0, /* No rhythm channel (9) */
- HW_CHANNELS_NR, /* # of voices */
- {FREQUENCY, SFX_PCM_STEREO_LR, SFX_PCM_FORMAT_S16_NATIVE}
+class MidiPlayer_Amiga : public MidiPlayer {
+public:
+ MidiPlayer_Amiga() { _driver = new MidiDriver_Amiga(g_system->getMixer()); }
+ int getPlayMask() const { return 0x40; }
+ int getPolyphony() const { return MidiDriver_Amiga::kVoices; }
+ bool hasRhythmChannel() const { return false; }
+ void setVolume(byte volume) { static_cast<MidiDriver_Amiga *>(_driver)->setVolume(volume); }
+ void playSwitch(bool play) { static_cast<MidiDriver_Amiga *>(_driver)->playSwitch(play); }
+ void loadInstrument(int idx, byte *data);
};
+MidiPlayer *MidiPlayer_Amiga_create() {
+ return new MidiPlayer_Amiga();
+}
+
} // End of namespace Sci