mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-12 06:00:48 +00:00
SCI: Add initial support for SCI0 Mac and SCI1 Amiga/Mac sound.
SCI1 sound is pretty much done, but envelope support is still not 100% complete. SCI0 Mac sound is not complete, but sounds "ok" for now. Much thanks to Walter, who did a ton of work on this. svn-id: r51001
This commit is contained in:
parent
a2ea3381fe
commit
6eedb73dba
@ -25,6 +25,7 @@
|
||||
|
||||
#include "sound/softsynth/emumidi.h"
|
||||
#include "sci/sound/drivers/mididriver.h"
|
||||
#include "sci/resource.h"
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/frac.h"
|
||||
@ -34,27 +35,6 @@ namespace Sci {
|
||||
|
||||
/* #define DEBUG */
|
||||
|
||||
// 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,
|
||||
147, 156, 165, 175, 185, 196, 208, 220,
|
||||
234, 248, 262, 278, 294, 312, 331, 350,
|
||||
371, 393, 417, 441, 468, 496, 525, 556,
|
||||
589, 625, 662, 701, 743, 787, 834, 883,
|
||||
936, 992, 1051, 1113, 1179, 1250, 1324, 1403,
|
||||
1486, 1574, 1668, 1767, 1872, 1984, 2102, 2227,
|
||||
2359, 2500, 2648, 2806, 2973, 3149, 3337, 3535,
|
||||
3745, 3968, 4204, 4454, 4719, 5000, 5297, 5612,
|
||||
5946, 6299, 6674, 7071, 7491, 7937, 8408, 8908,
|
||||
9438, 10000, 10594, 11224, 11892, 12599, 13348, 14142,
|
||||
14983, 15874, 16817, 17817, 18877, 20000, 21189, 22449,
|
||||
23784, 25198, 26696, 28284, 29966, 31748, 33635, 35635,
|
||||
37754, 40000, 42378, 44898, 47568, 50396, 53393, 56568,
|
||||
59932, 63496, 67271, 71271, 75509, 80000, 84757, 89796
|
||||
};
|
||||
|
||||
class MidiDriver_Amiga : public MidiDriver_Emulated {
|
||||
public:
|
||||
enum {
|
||||
@ -99,6 +79,7 @@ private:
|
||||
int instrument;
|
||||
int volume;
|
||||
int pan;
|
||||
uint16 pitch;
|
||||
};
|
||||
|
||||
struct Envelope {
|
||||
@ -121,7 +102,7 @@ private:
|
||||
frac_t rate;
|
||||
};
|
||||
|
||||
struct Instrument {
|
||||
struct InstrumentSample {
|
||||
char name[30];
|
||||
int mode;
|
||||
int size; // Size of non-looping part in bytes
|
||||
@ -130,32 +111,52 @@ private:
|
||||
Envelope envelope[4]; // Envelope
|
||||
int8 *samples;
|
||||
int8 *loop;
|
||||
int16 startNote;
|
||||
int16 endNote;
|
||||
bool isUnsigned;
|
||||
uint16 baseFreq;
|
||||
uint16 baseNote;
|
||||
int16 fixedNote;
|
||||
};
|
||||
|
||||
class Instrument : public Common::Array<InstrumentSample *> {
|
||||
public:
|
||||
char name[30];
|
||||
};
|
||||
|
||||
struct Bank {
|
||||
char name[30];
|
||||
uint size;
|
||||
Instrument *instruments[256];
|
||||
Common::Array<Instrument> instruments;
|
||||
};
|
||||
|
||||
bool _isSci1;
|
||||
bool _playSwitch;
|
||||
int _masterVolume;
|
||||
int _frequency;
|
||||
Envelope _envDecay;
|
||||
Bank _bank; // Instrument bank
|
||||
double _freqTable[48];
|
||||
|
||||
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 setOutputFrac(int voice);
|
||||
int interpolate(int8 *samples, frac_t offset, bool isUnsigned);
|
||||
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);
|
||||
InstrumentSample *findInstrument(int instrument, int note);
|
||||
void pitchWheel(int ch, uint16 pitch);
|
||||
|
||||
bool loadInstrumentsSCI0(Common::File &file);
|
||||
bool loadInstrumentsSCI0Mac(Common::SeekableReadStream &file);
|
||||
InstrumentSample *readInstrumentSCI0(Common::SeekableReadStream &file, int *id);
|
||||
bool loadInstrumentsSCI1(Common::SeekableReadStream &file);
|
||||
};
|
||||
|
||||
void MidiDriver_Amiga::setEnvelope(Voice *channel, Envelope *envelope, int phase) {
|
||||
@ -168,25 +169,32 @@ void MidiDriver_Amiga::setEnvelope(Voice *channel, Envelope *envelope, int phase
|
||||
channel->velocity = envelope[phase - 1].target;
|
||||
}
|
||||
|
||||
int MidiDriver_Amiga::interpolate(int8 *samples, frac_t offset) {
|
||||
int MidiDriver_Amiga::interpolate(int8 *samples, frac_t offset, bool isUnsigned) {
|
||||
int x = fracToInt(offset);
|
||||
int diff = (samples[x + 1] - samples[x]) << 8;
|
||||
|
||||
if (isUnsigned) {
|
||||
int s1 = (byte)samples[x] - 0x80;
|
||||
int s2 = (byte)samples[x + 1] - 0x80;
|
||||
int diff = (s2 - s1) << 8;
|
||||
return (s1 << 8) + fracToInt(diff * (offset & FRAC_LO_MASK));
|
||||
}
|
||||
|
||||
int diff = (samples[x + 1] - samples[x]) << 8;
|
||||
return (samples[x] << 8) + fracToInt(diff * (offset & FRAC_LO_MASK));
|
||||
}
|
||||
|
||||
void MidiDriver_Amiga::playInstrument(int16 *dest, Voice *channel, int count) {
|
||||
int index = 0;
|
||||
int vol = _channels[channel->hw_channel].volume;
|
||||
Instrument *instrument = _bank.instruments[channel->instrument];
|
||||
InstrumentSample *instrument = findInstrument(channel->instrument, channel->note);
|
||||
|
||||
while (1) {
|
||||
/* Available source samples until end of segment */
|
||||
frac_t lin_avail;
|
||||
int seg_end, rem, i, amount;
|
||||
uint32 seg_end, rem, i, amount;
|
||||
int8 *samples;
|
||||
|
||||
if (channel->looping) {
|
||||
if (channel->looping && instrument->loop) {
|
||||
samples = instrument->loop;
|
||||
seg_end = instrument->loop_size;
|
||||
} else {
|
||||
@ -208,11 +216,11 @@ void MidiDriver_Amiga::playInstrument(int16 *dest, Voice *channel, int count) {
|
||||
amount = rem;
|
||||
|
||||
/* Stop at next envelope event */
|
||||
if ((channel->envelope_samples != -1) && (amount > channel->envelope_samples))
|
||||
if ((channel->envelope_samples != -1) && (amount > (uint32)channel->envelope_samples))
|
||||
amount = channel->envelope_samples;
|
||||
|
||||
for (i = 0; i < amount; i++) {
|
||||
dest[index++] = interpolate(samples, channel->offset) * channel->velocity / 64 * channel->note_velocity * vol / (127 * 127);
|
||||
dest[index++] = interpolate(samples, channel->offset, instrument->isUnsigned) * channel->velocity / 64 * channel->note_velocity * vol / (127 * 127);
|
||||
channel->offset += channel->rate;
|
||||
}
|
||||
|
||||
@ -263,7 +271,7 @@ void MidiDriver_Amiga::playInstrument(int16 *dest, Voice *channel, int count) {
|
||||
if (index == count)
|
||||
break;
|
||||
|
||||
if (fracToInt(channel->offset) >= seg_end) {
|
||||
if ((uint32)fracToInt(channel->offset) >= seg_end) {
|
||||
if (instrument->mode & kModeLoop) {
|
||||
/* Loop the samples */
|
||||
channel->offset -= intToFrac(seg_end);
|
||||
@ -279,7 +287,7 @@ void MidiDriver_Amiga::playInstrument(int16 *dest, Voice *channel, int count) {
|
||||
|
||||
void MidiDriver_Amiga::changeInstrument(int channel, int instrument) {
|
||||
#ifdef DEBUG
|
||||
if (_bank.instruments[instrument])
|
||||
if (_bank.instruments[instrument][0])
|
||||
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);
|
||||
@ -300,9 +308,16 @@ void MidiDriver_Amiga::stopChannel(int ch) {
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_Amiga::pitchWheel(int ch, uint16 pitch) {
|
||||
_channels[ch].pitch = pitch;
|
||||
|
||||
for (int i = 0; i < kChannels; i++)
|
||||
if (_voices[i].note != -1 && _voices[i].hw_channel == ch)
|
||||
setOutputFrac(i);
|
||||
}
|
||||
|
||||
void MidiDriver_Amiga::stopNote(int ch, int note) {
|
||||
int channel;
|
||||
Instrument *instrument;
|
||||
|
||||
for (channel = 0; channel < kChannels; channel++)
|
||||
if (_voices[channel].note == note && _voices[channel].hw_channel == ch && !_voices[channel].decay)
|
||||
@ -315,15 +330,75 @@ void MidiDriver_Amiga::stopNote(int ch, int note) {
|
||||
return;
|
||||
}
|
||||
|
||||
instrument = _bank.instruments[_voices[channel].instrument];
|
||||
InstrumentSample *instrument = findInstrument(_voices[channel].instrument, note);
|
||||
|
||||
// FIXME: SCI1 envelope support is not perfect yet
|
||||
|
||||
/* Start the envelope phases for note-off if looping is on and envelope is enabled */
|
||||
if ((instrument->mode & kModeLoop) && (instrument->envelope[0].length != 0))
|
||||
setEnvelope(&_voices[channel], instrument->envelope, 2);
|
||||
}
|
||||
|
||||
MidiDriver_Amiga::InstrumentSample *MidiDriver_Amiga::findInstrument(int instrument, int note) {
|
||||
if ((uint)instrument >= _bank.instruments.size())
|
||||
return 0;
|
||||
|
||||
for (uint32 i = 0; i < _bank.instruments[instrument].size(); i++) {
|
||||
InstrumentSample *sample = _bank.instruments[instrument][i];
|
||||
if (note >= sample->startNote && note <= sample->endNote)
|
||||
return sample;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MidiDriver_Amiga::setOutputFrac(int voice) {
|
||||
InstrumentSample *instrument = findInstrument(_voices[voice].instrument, _voices[voice].note);
|
||||
|
||||
int fnote = 0;
|
||||
|
||||
if (instrument->fixedNote == -1) {
|
||||
fnote = _voices[voice].note;
|
||||
|
||||
// Handle SCI0-style transposing here
|
||||
if (!_isSci1)
|
||||
fnote += instrument->transpose;
|
||||
|
||||
if (fnote < 0 || fnote > 127) {
|
||||
warning("[sfx:seq:amiga] illegal note %i", fnote);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
fnote = instrument->fixedNote;
|
||||
|
||||
// Compute rate for note
|
||||
int mulFact = 1, divFact = 1;
|
||||
|
||||
fnote -= instrument->baseNote;
|
||||
fnote *= 4;
|
||||
// FIXME: check how SSCI maps this
|
||||
fnote += (_channels[_voices[voice].hw_channel].pitch - 0x2000) / 169;
|
||||
|
||||
while (fnote < 0) {
|
||||
divFact *= 2;
|
||||
fnote += 12 * 4;
|
||||
}
|
||||
|
||||
while (fnote >= 12 * 4) {
|
||||
mulFact *= 2;
|
||||
fnote -= 12 * 4;
|
||||
}
|
||||
|
||||
double freq = _freqTable[fnote] * instrument->baseFreq * mulFact / divFact;
|
||||
|
||||
// Handle SCI1-style transposing here
|
||||
if (instrument->transpose && _isSci1)
|
||||
freq = freq + ((_freqTable[4] - 1.0) * freq * (double)instrument->transpose / (double)16);
|
||||
|
||||
_voices[voice].rate = doubleToFrac(freq / _frequency);
|
||||
}
|
||||
|
||||
void MidiDriver_Amiga::startNote(int ch, int note, int velocity) {
|
||||
Instrument *instrument;
|
||||
int channel;
|
||||
|
||||
if (_channels[ch].instrument < 0 || _channels[ch].instrument > 255) {
|
||||
@ -331,7 +406,7 @@ void MidiDriver_Amiga::startNote(int ch, int note, int velocity) {
|
||||
return;
|
||||
}
|
||||
|
||||
instrument = _bank.instruments[_channels[ch].instrument];
|
||||
InstrumentSample *instrument = findInstrument(_channels[ch].instrument, note);
|
||||
|
||||
if (!instrument) {
|
||||
warning("[sfx:seq:amiga] instrument %i does not exist", _channels[ch].instrument);
|
||||
@ -349,19 +424,6 @@ void MidiDriver_Amiga::startNote(int ch, int note, int velocity) {
|
||||
|
||||
stopChannel(ch);
|
||||
|
||||
if (instrument->mode & kModePitch) {
|
||||
int fnote = note + instrument->transpose;
|
||||
|
||||
if (fnote < 0 || fnote > 127) {
|
||||
warning("[sfx:seq:amiga] illegal note %i\n", fnote);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Compute rate for note */
|
||||
_voices[channel].rate = doubleToFrac(freq_table[fnote] / (double) _frequency);
|
||||
} else
|
||||
_voices[channel].rate = doubleToFrac(kBaseFreq / (double) _frequency);
|
||||
|
||||
_voices[channel].instrument = _channels[ch].instrument;
|
||||
_voices[channel].note = note;
|
||||
_voices[channel].note_velocity = velocity;
|
||||
@ -377,30 +439,34 @@ void MidiDriver_Amiga::startNote(int ch, int note, int velocity) {
|
||||
_voices[channel].hw_channel = ch;
|
||||
_voices[channel].decay = 0;
|
||||
_voices[channel].looping = 0;
|
||||
setOutputFrac(channel);
|
||||
}
|
||||
|
||||
MidiDriver_Amiga::Instrument *MidiDriver_Amiga::readInstrument(Common::File &file, int *id) {
|
||||
Instrument *instrument;
|
||||
MidiDriver_Amiga::InstrumentSample *MidiDriver_Amiga::readInstrumentSCI0(Common::SeekableReadStream &file, int *id) {
|
||||
byte header[61];
|
||||
int size;
|
||||
int seg_size[3];
|
||||
int loop_offset;
|
||||
int i;
|
||||
|
||||
if (file.read(header, 61) < 61) {
|
||||
warning("[sfx:seq:amiga] failed to read instrument header");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
instrument = new Instrument;
|
||||
|
||||
int seg_size[3];
|
||||
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;
|
||||
|
||||
InstrumentSample *instrument = new InstrumentSample;
|
||||
|
||||
instrument->startNote = 0;
|
||||
instrument->endNote = 127;
|
||||
instrument->isUnsigned = false;
|
||||
instrument->baseFreq = kBaseFreq;
|
||||
instrument->baseNote = 101;
|
||||
instrument->fixedNote = 101;
|
||||
|
||||
instrument->mode = header[33];
|
||||
instrument->transpose = (int8) header[34];
|
||||
for (i = 0; i < 4; i++) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int length = (int8) header[49 + i];
|
||||
|
||||
if (length == 0 && i > 0)
|
||||
@ -413,13 +479,14 @@ MidiDriver_Amiga::Instrument *MidiDriver_Amiga::readInstrument(Common::File &fil
|
||||
/* Final target must be 0 */
|
||||
instrument->envelope[3].target = 0;
|
||||
|
||||
loop_offset = READ_BE_UINT32(header + 37) & ~1;
|
||||
size = seg_size[0] + seg_size[1] + seg_size[2];
|
||||
int loop_offset = READ_BE_UINT32(header + 37) & ~1;
|
||||
int size = seg_size[0] + seg_size[1] + seg_size[2];
|
||||
|
||||
*id = READ_BE_UINT16(header);
|
||||
|
||||
strncpy(instrument->name, (char *) header + 2, 29);
|
||||
instrument->name[29] = 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("[sfx:seq:amiga] Reading instrument %i: \"%s\" (%i bytes)\n",
|
||||
*id, instrument->name, size);
|
||||
@ -429,6 +496,7 @@ MidiDriver_Amiga::Instrument *MidiDriver_Amiga::readInstrument(Common::File &fil
|
||||
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
|
||||
|
||||
instrument->samples = (int8 *) malloc(size + 1);
|
||||
if (file.read(instrument->samples, size) < (unsigned int)size) {
|
||||
warning("[sfx:seq:amiga] failed to read instrument samples");
|
||||
@ -437,6 +505,9 @@ MidiDriver_Amiga::Instrument *MidiDriver_Amiga::readInstrument(Common::File &fil
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (instrument->mode & kModePitch)
|
||||
instrument->fixedNote = -1;
|
||||
|
||||
if (instrument->mode & kModeLoop) {
|
||||
if (loop_offset + seg_size[1] > size) {
|
||||
#ifdef DEBUG
|
||||
@ -463,6 +534,7 @@ MidiDriver_Amiga::Instrument *MidiDriver_Amiga::readInstrument(Common::File &fil
|
||||
instrument->loop[instrument->loop_size] = instrument->loop[0];
|
||||
} else {
|
||||
instrument->loop = NULL;
|
||||
instrument->loop_size = 0;
|
||||
instrument->size = size;
|
||||
instrument->samples[instrument->size] = 0;
|
||||
}
|
||||
@ -483,27 +555,16 @@ uint32 MidiDriver_Amiga::property(int prop, uint32 param) {
|
||||
}
|
||||
|
||||
int MidiDriver_Amiga::open() {
|
||||
_isSci1 = false;
|
||||
|
||||
for (int i = 0; i < 48; i++)
|
||||
_freqTable[i] = pow(2, i / (double)48);
|
||||
|
||||
_frequency = _mixer->getOutputRate();
|
||||
_envDecay.length = _frequency / (32 * 64);
|
||||
_envDecay.delta = 1;
|
||||
_envDecay.target = 0;
|
||||
|
||||
Common::File file;
|
||||
byte header[40];
|
||||
|
||||
if (!file.open("bank.001")) {
|
||||
warning("[sfx:seq:amiga] file bank.001 not found");
|
||||
return Common::kUnknownError;
|
||||
}
|
||||
|
||||
if (file.read(header, 40) < 40) {
|
||||
warning("[sfx:seq:amiga] failed to read header of file bank.001");
|
||||
return Common::kUnknownError;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < 256; i++)
|
||||
_bank.instruments[i] = NULL;
|
||||
|
||||
for (uint i = 0; i < kChannels; i++) {
|
||||
_voices[i].note = -1;
|
||||
_voices[i].hw_channel = 0;
|
||||
@ -513,32 +574,46 @@ int MidiDriver_Amiga::open() {
|
||||
_channels[i].instrument = -1;
|
||||
_channels[i].volume = 127;
|
||||
_channels[i].pan = (i % 4 == 0 || i % 4 == 3 ? kPanLeft : kPanRight);
|
||||
_channels[i].pitch = 0x2000;
|
||||
}
|
||||
|
||||
_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);
|
||||
#endif
|
||||
Common::File file;
|
||||
|
||||
for (uint i = 0; i < _bank.size; i++) {
|
||||
int id;
|
||||
Instrument *instrument = readInstrument(file, &id);
|
||||
if (file.open("bank.001")) {
|
||||
if (!loadInstrumentsSCI0(file)) {
|
||||
file.close();
|
||||
return Common::kUnknownError;
|
||||
}
|
||||
file.close();
|
||||
} else {
|
||||
ResourceManager *resMan = g_sci->getResMan();
|
||||
|
||||
if (!instrument) {
|
||||
warning("[sfx:seq:amiga] failed to read bank.001");
|
||||
Resource *resource = resMan->findResource(ResourceId(kResourceTypePatch, 7), false);
|
||||
if (!resource)
|
||||
resource = resMan->findResource(ResourceId(kResourceTypePatch, 9), false);
|
||||
|
||||
// If we have a patch by this point, it's SCI1
|
||||
if (resource)
|
||||
_isSci1 = true;
|
||||
|
||||
// Check for the SCI0 Mac patch
|
||||
if (!resource)
|
||||
resource = resMan->findResource(ResourceId(kResourceTypePatch, 200), false);
|
||||
|
||||
if (!resource) {
|
||||
warning("Could not open patch for Amiga sound driver");
|
||||
return Common::kUnknownError;
|
||||
}
|
||||
|
||||
if (id < 0 || id > 255) {
|
||||
warning("[sfx:seq:amiga] Error: instrument ID out of bounds");
|
||||
Common::MemoryReadStream stream(resource->data, resource->size);
|
||||
|
||||
if (_isSci1) {
|
||||
if (!loadInstrumentsSCI1(stream))
|
||||
return Common::kUnknownError;
|
||||
} else if (!loadInstrumentsSCI0Mac(stream))
|
||||
return Common::kUnknownError;
|
||||
}
|
||||
|
||||
_bank.instruments[id] = instrument;
|
||||
}
|
||||
|
||||
|
||||
MidiDriver_Emulated::open();
|
||||
|
||||
_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO);
|
||||
@ -550,11 +625,13 @@ void MidiDriver_Amiga::close() {
|
||||
_mixer->stopHandle(_mixerSoundHandle);
|
||||
|
||||
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];
|
||||
for (uint32 j = 0; j < _bank.instruments[i].size(); j++) {
|
||||
if (_bank.instruments[i][j]) {
|
||||
if (_bank.instruments[i][j]->loop)
|
||||
free(_bank.instruments[i][j]->loop);
|
||||
free(_bank.instruments[i][j]->samples);
|
||||
delete _bank.instruments[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -603,6 +680,9 @@ void MidiDriver_Amiga::send(uint32 b) {
|
||||
case 0xc0:
|
||||
changeInstrument(channel, op1);
|
||||
break;
|
||||
case 0xe0:
|
||||
pitchWheel(channel, (op2 << 7) | op1);
|
||||
break;
|
||||
default:
|
||||
warning("[sfx:seq:amiga] unknown event %02x", command);
|
||||
}
|
||||
@ -651,6 +731,239 @@ void MidiDriver_Amiga::generateSamples(int16 *data, int len) {
|
||||
free(buffers);
|
||||
}
|
||||
|
||||
bool MidiDriver_Amiga::loadInstrumentsSCI0(Common::File &file) {
|
||||
_isSci1 = false;
|
||||
|
||||
byte header[40];
|
||||
|
||||
if (file.read(header, 40) < 40) {
|
||||
warning("[sfx:seq:amiga] failed to read header of file bank.001");
|
||||
return false;
|
||||
}
|
||||
|
||||
_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);
|
||||
#endif
|
||||
|
||||
for (uint i = 0; i < _bank.size; i++) {
|
||||
int id;
|
||||
InstrumentSample *instrument = readInstrumentSCI0(file, &id);
|
||||
|
||||
if (!instrument) {
|
||||
warning("[sfx:seq:amiga] failed to read bank.001");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (id < 0 || id > 255) {
|
||||
warning("[sfx:seq:amiga] Error: instrument ID out of bounds");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((uint)id >= _bank.instruments.size())
|
||||
_bank.instruments.resize(id + 1);
|
||||
|
||||
_bank.instruments[id].push_back(instrument);
|
||||
memcpy(_bank.instruments[id].name, instrument->name, sizeof(instrument->name));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MidiDriver_Amiga::loadInstrumentsSCI0Mac(Common::SeekableReadStream &file) {
|
||||
byte header[40];
|
||||
|
||||
if (file.read(header, 40) < 40) {
|
||||
warning("[sfx:seq:amiga] failed to read header of file patch.200");
|
||||
return false;
|
||||
}
|
||||
|
||||
_bank.size = 128;
|
||||
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);
|
||||
#endif
|
||||
|
||||
Common::Array<uint32> instrumentOffsets;
|
||||
instrumentOffsets.resize(_bank.size);
|
||||
_bank.instruments.resize(_bank.size);
|
||||
|
||||
for (uint32 i = 0; i < _bank.size; i++)
|
||||
instrumentOffsets[i] = file.readUint32BE();
|
||||
|
||||
for (uint i = 0; i < _bank.size; i++) {
|
||||
// 0 signifies it doesn't exist
|
||||
if (instrumentOffsets[i] == 0)
|
||||
continue;
|
||||
|
||||
file.seek(instrumentOffsets[i]);
|
||||
|
||||
uint16 id = file.readUint16BE();
|
||||
if (id != i)
|
||||
error("Instrument number mismatch");
|
||||
|
||||
InstrumentSample *instrument = new InstrumentSample;
|
||||
|
||||
instrument->startNote = 0;
|
||||
instrument->endNote = 127;
|
||||
instrument->isUnsigned = true;
|
||||
instrument->baseFreq = kBaseFreq;
|
||||
instrument->baseNote = 101;
|
||||
instrument->fixedNote = 101;
|
||||
instrument->mode = file.readUint16BE();
|
||||
|
||||
// Read in the offsets
|
||||
int32 seg_size[3];
|
||||
seg_size[0] = file.readUint32BE();
|
||||
seg_size[1] = file.readUint32BE();
|
||||
seg_size[2] = file.readUint32BE();
|
||||
|
||||
instrument->transpose = file.readUint16BE();
|
||||
|
||||
for (byte j = 0; j < 4; j++) {
|
||||
int length = (int8)file.readByte();
|
||||
|
||||
if (length == 0 && j > 0)
|
||||
length = 256;
|
||||
|
||||
instrument->envelope[j].length = length * _frequency / 60;
|
||||
instrument->envelope[j].delta = (int8)file.readByte();
|
||||
instrument->envelope[j].target = file.readByte();
|
||||
}
|
||||
|
||||
// Final target must be 0
|
||||
instrument->envelope[3].target = 0;
|
||||
|
||||
file.read(instrument->name, 30);
|
||||
|
||||
if (instrument->mode & kModePitch)
|
||||
instrument->fixedNote = -1;
|
||||
|
||||
uint32 size = seg_size[2];
|
||||
uint32 loop_offset = seg_size[0];
|
||||
|
||||
instrument->samples = (int8 *)malloc(size + 1);
|
||||
uint32 startPos = file.pos();
|
||||
if (file.read(instrument->samples, size) < size) {
|
||||
warning("[sfx:seq:amiga] failed to read instrument sample");
|
||||
free(instrument->samples);
|
||||
delete instrument;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (instrument->mode & kModeLoop) {
|
||||
instrument->size = seg_size[0];
|
||||
instrument->loop_size = seg_size[1] - seg_size[0];
|
||||
|
||||
instrument->loop = (int8*)malloc(instrument->loop_size + 1);
|
||||
memcpy(instrument->loop, instrument->samples + loop_offset, instrument->loop_size);
|
||||
|
||||
instrument->samples[instrument->size] = instrument->loop[0];
|
||||
instrument->loop[instrument->loop_size] = instrument->loop[0];
|
||||
} else {
|
||||
instrument->loop = NULL;
|
||||
instrument->loop_size = 0;
|
||||
instrument->size = size;
|
||||
instrument->samples[instrument->size] = (int8)0x80;
|
||||
}
|
||||
|
||||
_bank.instruments[id].push_back(instrument);
|
||||
memcpy(_bank.instruments[id].name, instrument->name, sizeof(instrument->name));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MidiDriver_Amiga::loadInstrumentsSCI1(Common::SeekableReadStream &file) {
|
||||
_bank.size = 128;
|
||||
|
||||
Common::Array<uint32> instrumentOffsets;
|
||||
instrumentOffsets.resize(_bank.size);
|
||||
_bank.instruments.resize(_bank.size);
|
||||
|
||||
for (uint32 i = 0; i < _bank.size; i++)
|
||||
instrumentOffsets[i] = file.readUint32BE();
|
||||
|
||||
for (uint32 i = 0; i < _bank.size; i++) {
|
||||
// 0 signifies it doesn't exist
|
||||
if (instrumentOffsets[i] == 0)
|
||||
continue;
|
||||
|
||||
file.seek(instrumentOffsets[i]);
|
||||
|
||||
// Read in the instrument name
|
||||
file.read(_bank.instruments[i].name, 10); // last two bytes are always 0
|
||||
|
||||
for (uint32 j = 0; ; j++) {
|
||||
InstrumentSample *sample = new InstrumentSample;
|
||||
memset(sample, 0, sizeof(InstrumentSample));
|
||||
|
||||
sample->startNote = file.readSint16BE();
|
||||
|
||||
// startNote being -1 signifies we're done with this instrument
|
||||
if (sample->startNote == -1) {
|
||||
delete sample;
|
||||
break;
|
||||
}
|
||||
|
||||
sample->endNote = file.readSint16BE();
|
||||
uint32 samplePtr = file.readUint32BE();
|
||||
sample->transpose = file.readSint16BE();
|
||||
for (int env = 0; env < 3; env++) {
|
||||
sample->envelope[env].length = file.readByte() * _frequency / 60;
|
||||
sample->envelope[env].delta = (env == 0 ? 10 : -10);
|
||||
sample->envelope[env].target = file.readByte();
|
||||
}
|
||||
|
||||
sample->envelope[3].length = 0;
|
||||
sample->fixedNote = file.readSint16BE();
|
||||
int16 loop = file.readSint16BE();
|
||||
uint32 nextSamplePos = file.pos();
|
||||
|
||||
file.seek(samplePtr);
|
||||
file.read(sample->name, 8);
|
||||
|
||||
sample->isUnsigned = file.readUint16BE() == 0;
|
||||
uint16 phase1Offset = file.readUint16BE();
|
||||
uint16 phase1End = file.readUint16BE();
|
||||
uint16 phase2Offset = file.readUint16BE();
|
||||
uint16 phase2End = file.readUint16BE();
|
||||
sample->baseNote = file.readUint16BE();
|
||||
uint32 periodTableOffset = file.readUint32BE();
|
||||
uint32 sampleDataPos = file.pos();
|
||||
|
||||
sample->size = phase1End - phase1Offset + 1;
|
||||
sample->loop_size = phase2End - phase2Offset + 1;
|
||||
|
||||
sample->samples = (int8 *)malloc(sample->size + 1);
|
||||
file.seek(phase1Offset + sampleDataPos);
|
||||
file.read(sample->samples, sample->size);
|
||||
sample->samples[sample->size] = (sample->isUnsigned ? (int8)0x80 : 0);
|
||||
|
||||
if (loop == 0 && sample->loop_size > 1) {
|
||||
sample->loop = (int8 *)malloc(sample->loop_size + 1);
|
||||
file.seek(phase2Offset + sampleDataPos);
|
||||
file.read(sample->loop, sample->loop_size);
|
||||
sample->mode |= kModeLoop;
|
||||
sample->samples[sample->size] = sample->loop[0];
|
||||
sample->loop[sample->loop_size] = sample->loop[0];
|
||||
}
|
||||
|
||||
_bank.instruments[i].push_back(sample);
|
||||
|
||||
file.seek(periodTableOffset + 0xe0);
|
||||
sample->baseFreq = file.readUint16BE();
|
||||
|
||||
file.seek(nextSamplePos);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class MidiPlayer_Amiga : public MidiPlayer {
|
||||
public:
|
||||
MidiPlayer_Amiga(SciVersion version) : MidiPlayer(version) { _driver = new MidiDriver_Amiga(g_system->getMixer()); }
|
||||
@ -667,8 +980,8 @@ MidiPlayer *MidiPlayer_Amiga_create(SciVersion version) {
|
||||
}
|
||||
|
||||
byte MidiPlayer_Amiga::getPlayId() {
|
||||
if (_version != SCI_VERSION_0_LATE)
|
||||
error("Amiga sound support not available for this SCI version");
|
||||
if (_version > SCI_VERSION_0_LATE)
|
||||
return 0x06;
|
||||
|
||||
return 0x40;
|
||||
}
|
||||
|
@ -63,22 +63,14 @@ void SciMusic::init() {
|
||||
// SCI sound init
|
||||
_dwTempo = 0;
|
||||
|
||||
// Default to MIDI in SCI32 games, as many don't have AdLib support.
|
||||
// WORKAROUND: Default to MIDI in Amiga SCI1_EGA+ games as we don't support
|
||||
// those patches yet. We also don't yet support the 7.pat file of SCI1+ Mac
|
||||
// games or SCI0 Mac patches, so we default to MIDI in those games to let
|
||||
// them run.
|
||||
// Default to MIDI in SCI2.1+ games, as many don't have AdLib support.
|
||||
Common::Platform platform = g_sci->getPlatform();
|
||||
uint32 dev = MidiDriver::detectDevice(
|
||||
(getSciVersion() >= SCI_VERSION_2 || platform == Common::kPlatformMacintosh ||
|
||||
(platform == Common::kPlatformAmiga && getSciVersion() >= SCI_VERSION_1_EGA))
|
||||
? (MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM)
|
||||
: (MDT_PCSPK | MDT_ADLIB | MDT_MIDI));
|
||||
uint32 dev = MidiDriver::detectDevice((getSciVersion() >= SCI_VERSION_2_1) ? (MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM) : (MDT_PCSPK | MDT_ADLIB | MDT_MIDI));
|
||||
|
||||
switch (MidiDriver::getMusicType(dev)) {
|
||||
case MT_ADLIB:
|
||||
// FIXME: There's no Amiga sound option, so we hook it up to AdLib
|
||||
if (g_sci->getPlatform() == Common::kPlatformAmiga)
|
||||
if (g_sci->getPlatform() == Common::kPlatformAmiga || platform == Common::kPlatformMacintosh)
|
||||
_pMidiDrv = MidiPlayer_Amiga_create(_soundVersion);
|
||||
else
|
||||
_pMidiDrv = MidiPlayer_AdLib_create(_soundVersion);
|
||||
|
Loading…
x
Reference in New Issue
Block a user