[Scummvm-cvs-logs] SF.net SVN: scummvm:[51006] scummvm/trunk/engines/sci

mthreepwood at users.sourceforge.net mthreepwood at users.sourceforge.net
Sun Jul 18 21:01:36 CEST 2010


Revision: 51006
          http://scummvm.svn.sourceforge.net/scummvm/?rev=51006&view=rev
Author:   mthreepwood
Date:     2010-07-18 19:01:35 +0000 (Sun, 18 Jul 2010)

Log Message:
-----------
SCI: Rename the Amiga music drivers to AmigaMac to reflect its current purpose.

Modified Paths:
--------------
    scummvm/trunk/engines/sci/module.mk
    scummvm/trunk/engines/sci/sound/drivers/mididriver.h
    scummvm/trunk/engines/sci/sound/music.cpp

Added Paths:
-----------
    scummvm/trunk/engines/sci/sound/drivers/amigamac.cpp

Removed Paths:
-------------
    scummvm/trunk/engines/sci/sound/drivers/amiga.cpp

Modified: scummvm/trunk/engines/sci/module.mk
===================================================================
--- scummvm/trunk/engines/sci/module.mk	2010-07-18 18:57:28 UTC (rev 51005)
+++ scummvm/trunk/engines/sci/module.mk	2010-07-18 19:01:35 UTC (rev 51006)
@@ -63,7 +63,7 @@
 	sound/music.o \
 	sound/soundcmd.o \
 	sound/drivers/adlib.o \
-	sound/drivers/amiga.o \
+	sound/drivers/amigamac.o \
 	sound/drivers/fb01.o \
 	sound/drivers/midi.o \
 	sound/drivers/pcjr.o \

Deleted: scummvm/trunk/engines/sci/sound/drivers/amiga.cpp
===================================================================
--- scummvm/trunk/engines/sci/sound/drivers/amiga.cpp	2010-07-18 18:57:28 UTC (rev 51005)
+++ scummvm/trunk/engines/sci/sound/drivers/amiga.cpp	2010-07-18 19:01:35 UTC (rev 51006)
@@ -1,988 +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 "sound/softsynth/emumidi.h"
-#include "sci/sound/drivers/mididriver.h"
-#include "sci/resource.h"
-
-#include "common/file.h"
-#include "common/frac.h"
-#include "common/util.h"
-
-namespace Sci {
-
-/* #define DEBUG */
-
-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;
-		uint16 pitch;
-	};
-
-	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 InstrumentSample {
-		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;
-		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;
-		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);
-	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);
-	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) {
-	channel->envelope = phase;
-	channel->envelope_samples = envelope[phase].length;
-
-	if (phase == 0)
-		channel->velocity = channel->note_velocity / 2;
-	else
-		channel->velocity = envelope[phase - 1].target;
-}
-
-int MidiDriver_Amiga::interpolate(int8 *samples, frac_t offset, bool isUnsigned) {
-	int x = fracToInt(offset);
-
-	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;
-	InstrumentSample *instrument = findInstrument(channel->instrument, channel->note);
-
-	while (1) {
-		/* Available source samples until end of segment */
-		frac_t lin_avail;
-		uint32 seg_end, rem, i, amount;
-		int8 *samples;
-
-		if (channel->looping && instrument->loop) {
-			samples = instrument->loop;
-			seg_end = instrument->loop_size;
-		} else {
-			samples = instrument->samples;
-			seg_end = instrument->size;
-		}
-
-		lin_avail = intToFrac(seg_end) - channel->offset;
-
-		rem = count - index;
-
-		/* Amount of destination samples that we will compute this iteration */
-		amount = lin_avail / channel->rate;
-
-		if (lin_avail % channel->rate)
-			amount++;
-
-		if (amount > rem)
-			amount = rem;
-
-		/* Stop at next envelope event */
-		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, instrument->isUnsigned) * channel->velocity / 64 * channel->note_velocity * vol / (127 * 127);
-			channel->offset += channel->rate;
-		}
-
-		if (channel->envelope_samples != -1)
-			channel->envelope_samples -= amount;
-
-		if (channel->envelope_samples == 0) {
-			Envelope *envelope;
-			int delta, target, velocity;
-
-			if (channel->decay)
-				envelope = &_envDecay;
-			else
-				envelope = &instrument->envelope[channel->envelope];
-
-			delta = envelope->delta;
-			target = envelope->target;
-			velocity = channel->velocity - envelope->delta;
-
-			/* Check whether we have reached the velocity target for the current phase */
-			if ((delta >= 0 && velocity <= target) || (delta < 0 && velocity >= target)) {
-				channel->velocity = target;
-
-				/* Stop note after velocity has dropped to 0 */
-				if (target == 0) {
-					channel->note = -1;
-					break;
-				} else
-					switch (channel->envelope) {
-					case 0:
-					case 2:
-						/* Go to next phase */
-						setEnvelope(channel, instrument->envelope, channel->envelope + 1);
-						break;
-					case 1:
-					case 3:
-						/* Stop envelope */
-						channel->envelope_samples = -1;
-						break;
-					}
-			} else {
-				/* We haven't reached the target yet */
-				channel->envelope_samples = envelope->length;
-				channel->velocity = velocity;
-			}
-		}
-
-		if (index == count)
-			break;
-
-		if ((uint32)fracToInt(channel->offset) >= seg_end) {
-			if (instrument->mode & kModeLoop) {
-				/* Loop the samples */
-				channel->offset -= intToFrac(seg_end);
-				channel->looping = 1;
-			} else {
-				/* All samples have been played */
-				channel->note = -1;
-				break;
-			}
-		}
-	}
-}
-
-void MidiDriver_Amiga::changeInstrument(int channel, int instrument) {
-#ifdef DEBUG
-	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);
-#endif
-	_channels[channel].instrument = instrument;
-}
-
-void MidiDriver_Amiga::stopChannel(int ch) {
-	int i;
-
-	/* Start decay phase for note on this hw channel, if any */
-	for (i = 0; i < kChannels; i++)
-		if (_voices[i].note != -1 && _voices[i].hw_channel == ch && !_voices[i].decay) {
-			/* Trigger fast decay envelope */
-			_voices[i].decay = 1;
-			_voices[i].envelope_samples = _envDecay.length;
-			break;
-		}
-}
-
-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;
-
-	for (channel = 0; channel < kChannels; channel++)
-		if (_voices[channel].note == note && _voices[channel].hw_channel == ch && !_voices[channel].decay)
-			break;
-
-	if (channel == kChannels) {
-#ifdef DEBUG
-		warning("[sfx:seq:amiga] cannot stop note %i on channel %i", note, ch);
-#endif
-		return;
-	}
-
-	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) {
-	int channel;
-
-	if (_channels[ch].instrument < 0 || _channels[ch].instrument > 255) {
-		warning("[sfx:seq:amiga] invalid instrument %i on channel %i", _channels[ch].instrument, ch);
-		return;
-	}
-
-	InstrumentSample *instrument = findInstrument(_channels[ch].instrument, note);
-
-	if (!instrument) {
-		warning("[sfx:seq:amiga] instrument %i does not exist", _channels[ch].instrument);
-		return;
-	}
-
-	for (channel = 0; channel < kChannels; channel++)
-		if (_voices[channel].note == -1)
-			break;
-
-	if (channel == kChannels) {
-		warning("[sfx:seq:amiga] could not find a free channel");
-		return;
-	}
-
-	stopChannel(ch);
-
-	_voices[channel].instrument = _channels[ch].instrument;
-	_voices[channel].note = note;
-	_voices[channel].note_velocity = velocity;
-
-	if ((instrument->mode & kModeLoop) && (instrument->envelope[0].length != 0))
-		setEnvelope(&_voices[channel], instrument->envelope, 0);
-	else {
-		_voices[channel].velocity = 64;
-		_voices[channel].envelope_samples = -1;
-	}
-
-	_voices[channel].offset = 0;
-	_voices[channel].hw_channel = ch;
-	_voices[channel].decay = 0;
-	_voices[channel].looping = 0;
-	setOutputFrac(channel);
-}
-
-MidiDriver_Amiga::InstrumentSample *MidiDriver_Amiga::readInstrumentSCI0(Common::SeekableReadStream &file, int *id) {
-	byte header[61];
-
-	if (file.read(header, 61) < 61) {
-		warning("[sfx:seq:amiga] failed to read instrument header");
-		return NULL;
-	}
-
-	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 (int i = 0; i < 4; i++) {
-		int length = (int8) header[49 + i];
-
-		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].target = header[57 + i];
-	}
-	/* Final target must be 0 */
-	instrument->envelope[3].target = 0;
-
-	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);
-	printf("                Mode: %02x\n", instrument->mode);
-	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
-
-	instrument->samples = (int8 *) malloc(size + 1);
-	if (file.read(instrument->samples, size) < (unsigned int)size) {
-		warning("[sfx:seq:amiga] failed to read instrument samples");
-		free(instrument->samples);
-		delete instrument;
-		return NULL;
-	}
-
-	if (instrument->mode & kModePitch)
-		instrument->fixedNote = -1;
-
-	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",
-			          loop_offset + seg_size[1] - size);
-#endif
-			seg_size[1] = size - loop_offset;
-		}
-
-		if (seg_size[1] < 0) {
-			warning("[sfx:seq:amiga] invalid looping point");
-			free(instrument->samples);
-			delete instrument;
-			return NULL;
-		}
-
-		instrument->size = seg_size[0];
-		instrument->loop_size = seg_size[1];
-
-		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] = 0;
-	}
-
-	return instrument;
-}
-
-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;
-}
-
-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;
-
-	for (uint i = 0; i < kChannels; i++) {
-		_voices[i].note = -1;
-		_voices[i].hw_channel = 0;
-	}
-
-	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);
-		_channels[i].pitch = 0x2000;
-	}
-
-	Common::File file;
-
-	if (file.open("bank.001")) {
-		if (!loadInstrumentsSCI0(file)) {
-			file.close();
-			return Common::kUnknownError;
-		}
-		file.close();
-	} else {
-		ResourceManager *resMan = g_sci->getResMan();
-
-		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;
-		}
-
-		Common::MemoryReadStream stream(resource->data, resource->size);
-
-		if (_isSci1) {
-			if (!loadInstrumentsSCI1(stream))
-				return Common::kUnknownError;
-		} else if (!loadInstrumentsSCI0Mac(stream))
-			return Common::kUnknownError;
-	}
-	
-	MidiDriver_Emulated::open();
-
-	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO);
-
-	return Common::kNoError;
-}
-
-void MidiDriver_Amiga::close() {
-	_mixer->stopHandle(_mixerSoundHandle);
-
-	for (uint i = 0; i < _bank.size; 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];
-			}
-		}
-	}
-}
-
-void MidiDriver_Amiga::playSwitch(bool play) {
-	_playSwitch = play;
-}
-
-void MidiDriver_Amiga::setVolume(byte volume_) {
-	_masterVolume = volume_;
-}
-
-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 (command) {
-	case 0x80:
-		stopNote(channel, op1);
-		break;
-	case 0x90:
-		if (op2 > 0)
-			startNote(channel, op1, op2);
-		else
-			stopNote(channel, op1);
-		break;
-	case 0xb0:
-		switch (op1) {
-		case 0x07:
-			_channels[channel].volume = op2;
-			break;
-		case 0x0a:
-#ifdef DEBUG
-			warning("[sfx:seq:amiga] ignoring pan 0x%02x event for channel %i", op2, channel);
-#endif
-			break;
-		case 0x7b:
-			stopChannel(channel);
-			break;
-		default:
-			warning("[sfx:seq:amiga] unknown control event 0x%02x", op1);
-		}
-		break;
-	case 0xc0:
-		changeInstrument(channel, op1);
-		break;
-	case 0xe0:
-		pitchWheel(channel, (op2 << 7) | op1);
-		break;
-	default:
-		warning("[sfx:seq:amiga] unknown event %02x", command);
-	}
-}
-
-void MidiDriver_Amiga::generateSamples(int16 *data, int len) {
-	if (len == 0)
-		return;
-
-	int16 *buffers = (int16*)malloc(len * 2 * kChannels);
-
-	memset(buffers, 0, len * 2 * kChannels);
-
-	/* 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;
-			}
-
-			/* 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;
-
-			/* Mix */
-			for (int i = 0; i < kChannels; i++)
-				mixed += buffers[i * len + j];
-
-			/* Adjust volume */
-			data[j] = mixed * _masterVolume >> 6;
-		}
-	}
-
-	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);
-		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()); }
-	byte getPlayId();
-	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(SciVersion version) {
-	return new MidiPlayer_Amiga(version);
-}
-
-byte MidiPlayer_Amiga::getPlayId() {
-	if (_version > SCI_VERSION_0_LATE)
-		return 0x06;
-
-	return 0x40;
-}
-
-} // End of namespace Sci

Copied: scummvm/trunk/engines/sci/sound/drivers/amigamac.cpp (from rev 51002, scummvm/trunk/engines/sci/sound/drivers/amiga.cpp)
===================================================================
--- scummvm/trunk/engines/sci/sound/drivers/amigamac.cpp	                        (rev 0)
+++ scummvm/trunk/engines/sci/sound/drivers/amigamac.cpp	2010-07-18 19:01:35 UTC (rev 51006)
@@ -0,0 +1,988 @@
+/* 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 "sound/softsynth/emumidi.h"
+#include "sci/sound/drivers/mididriver.h"
+#include "sci/resource.h"
+
+#include "common/file.h"
+#include "common/frac.h"
+#include "common/util.h"
+
+namespace Sci {
+
+/* #define DEBUG */
+
+class MidiDriver_AmigaMac : public MidiDriver_Emulated {
+public:
+	enum {
+		kVoices = 4
+	};
+
+	MidiDriver_AmigaMac(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer), _playSwitch(true), _masterVolume(15) { }
+	virtual ~MidiDriver_AmigaMac() { }
+
+	// 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;
+		uint16 pitch;
+	};
+
+	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 InstrumentSample {
+		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;
+		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;
+		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);
+	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);
+	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_AmigaMac::setEnvelope(Voice *channel, Envelope *envelope, int phase) {
+	channel->envelope = phase;
+	channel->envelope_samples = envelope[phase].length;
+
+	if (phase == 0)
+		channel->velocity = channel->note_velocity / 2;
+	else
+		channel->velocity = envelope[phase - 1].target;
+}
+
+int MidiDriver_AmigaMac::interpolate(int8 *samples, frac_t offset, bool isUnsigned) {
+	int x = fracToInt(offset);
+
+	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_AmigaMac::playInstrument(int16 *dest, Voice *channel, int count) {
+	int index = 0;
+	int vol = _channels[channel->hw_channel].volume;
+	InstrumentSample *instrument = findInstrument(channel->instrument, channel->note);
+
+	while (1) {
+		/* Available source samples until end of segment */
+		frac_t lin_avail;
+		uint32 seg_end, rem, i, amount;
+		int8 *samples;
+
+		if (channel->looping && instrument->loop) {
+			samples = instrument->loop;
+			seg_end = instrument->loop_size;
+		} else {
+			samples = instrument->samples;
+			seg_end = instrument->size;
+		}
+
+		lin_avail = intToFrac(seg_end) - channel->offset;
+
+		rem = count - index;
+
+		/* Amount of destination samples that we will compute this iteration */
+		amount = lin_avail / channel->rate;
+
+		if (lin_avail % channel->rate)
+			amount++;
+
+		if (amount > rem)
+			amount = rem;
+
+		/* Stop at next envelope event */
+		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, instrument->isUnsigned) * channel->velocity / 64 * channel->note_velocity * vol / (127 * 127);
+			channel->offset += channel->rate;
+		}
+
+		if (channel->envelope_samples != -1)
+			channel->envelope_samples -= amount;
+
+		if (channel->envelope_samples == 0) {
+			Envelope *envelope;
+			int delta, target, velocity;
+
+			if (channel->decay)
+				envelope = &_envDecay;
+			else
+				envelope = &instrument->envelope[channel->envelope];
+
+			delta = envelope->delta;
+			target = envelope->target;
+			velocity = channel->velocity - envelope->delta;
+
+			/* Check whether we have reached the velocity target for the current phase */
+			if ((delta >= 0 && velocity <= target) || (delta < 0 && velocity >= target)) {
+				channel->velocity = target;
+
+				/* Stop note after velocity has dropped to 0 */
+				if (target == 0) {
+					channel->note = -1;
+					break;
+				} else
+					switch (channel->envelope) {
+					case 0:
+					case 2:
+						/* Go to next phase */
+						setEnvelope(channel, instrument->envelope, channel->envelope + 1);
+						break;
+					case 1:
+					case 3:
+						/* Stop envelope */
+						channel->envelope_samples = -1;
+						break;
+					}
+			} else {
+				/* We haven't reached the target yet */
+				channel->envelope_samples = envelope->length;
+				channel->velocity = velocity;
+			}
+		}
+
+		if (index == count)
+			break;
+
+		if ((uint32)fracToInt(channel->offset) >= seg_end) {
+			if (instrument->mode & kModeLoop) {
+				/* Loop the samples */
+				channel->offset -= intToFrac(seg_end);
+				channel->looping = 1;
+			} else {
+				/* All samples have been played */
+				channel->note = -1;
+				break;
+			}
+		}
+	}
+}
+
+void MidiDriver_AmigaMac::changeInstrument(int channel, int instrument) {
+#ifdef DEBUG
+	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);
+#endif
+	_channels[channel].instrument = instrument;
+}
+
+void MidiDriver_AmigaMac::stopChannel(int ch) {
+	int i;
+
+	/* Start decay phase for note on this hw channel, if any */
+	for (i = 0; i < kChannels; i++)
+		if (_voices[i].note != -1 && _voices[i].hw_channel == ch && !_voices[i].decay) {
+			/* Trigger fast decay envelope */
+			_voices[i].decay = 1;
+			_voices[i].envelope_samples = _envDecay.length;
+			break;
+		}
+}
+
+void MidiDriver_AmigaMac::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_AmigaMac::stopNote(int ch, int note) {
+	int channel;
+
+	for (channel = 0; channel < kChannels; channel++)
+		if (_voices[channel].note == note && _voices[channel].hw_channel == ch && !_voices[channel].decay)
+			break;
+
+	if (channel == kChannels) {
+#ifdef DEBUG
+		warning("[sfx:seq:amiga] cannot stop note %i on channel %i", note, ch);
+#endif
+		return;
+	}
+
+	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_AmigaMac::InstrumentSample *MidiDriver_AmigaMac::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_AmigaMac::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_AmigaMac::startNote(int ch, int note, int velocity) {
+	int channel;
+
+	if (_channels[ch].instrument < 0 || _channels[ch].instrument > 255) {
+		warning("[sfx:seq:amiga] invalid instrument %i on channel %i", _channels[ch].instrument, ch);
+		return;
+	}
+
+	InstrumentSample *instrument = findInstrument(_channels[ch].instrument, note);
+
+	if (!instrument) {
+		warning("[sfx:seq:amiga] instrument %i does not exist", _channels[ch].instrument);
+		return;
+	}
+
+	for (channel = 0; channel < kChannels; channel++)
+		if (_voices[channel].note == -1)
+			break;
+
+	if (channel == kChannels) {
+		warning("[sfx:seq:amiga] could not find a free channel");
+		return;
+	}
+
+	stopChannel(ch);
+
+	_voices[channel].instrument = _channels[ch].instrument;
+	_voices[channel].note = note;
+	_voices[channel].note_velocity = velocity;
+
+	if ((instrument->mode & kModeLoop) && (instrument->envelope[0].length != 0))
+		setEnvelope(&_voices[channel], instrument->envelope, 0);
+	else {
+		_voices[channel].velocity = 64;
+		_voices[channel].envelope_samples = -1;
+	}
+
+	_voices[channel].offset = 0;
+	_voices[channel].hw_channel = ch;
+	_voices[channel].decay = 0;
+	_voices[channel].looping = 0;
+	setOutputFrac(channel);
+}
+
+MidiDriver_AmigaMac::InstrumentSample *MidiDriver_AmigaMac::readInstrumentSCI0(Common::SeekableReadStream &file, int *id) {
+	byte header[61];
+
+	if (file.read(header, 61) < 61) {
+		warning("[sfx:seq:amiga] failed to read instrument header");
+		return NULL;
+	}
+
+	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 (int i = 0; i < 4; i++) {
+		int length = (int8) header[49 + i];
+
+		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].target = header[57 + i];
+	}
+	/* Final target must be 0 */
+	instrument->envelope[3].target = 0;
+
+	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);
+	printf("                Mode: %02x\n", instrument->mode);
+	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
+
+	instrument->samples = (int8 *) malloc(size + 1);
+	if (file.read(instrument->samples, size) < (unsigned int)size) {
+		warning("[sfx:seq:amiga] failed to read instrument samples");
+		free(instrument->samples);
+		delete instrument;
+		return NULL;
+	}
+
+	if (instrument->mode & kModePitch)
+		instrument->fixedNote = -1;
+
+	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",
+			          loop_offset + seg_size[1] - size);
+#endif
+			seg_size[1] = size - loop_offset;
+		}
+
+		if (seg_size[1] < 0) {
+			warning("[sfx:seq:amiga] invalid looping point");
+			free(instrument->samples);
+			delete instrument;
+			return NULL;
+		}
+
+		instrument->size = seg_size[0];
+		instrument->loop_size = seg_size[1];
+
+		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] = 0;
+	}
+
+	return instrument;
+}
+
+uint32 MidiDriver_AmigaMac::property(int prop, uint32 param) {
+	switch(prop) {
+	case MIDI_PROP_MASTER_VOLUME:
+		if (param != 0xffff)
+			_masterVolume = param;
+		return _masterVolume;
+	default:
+		break;
+	}
+	return 0;
+}
+
+int MidiDriver_AmigaMac::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;
+
+	for (uint i = 0; i < kChannels; i++) {
+		_voices[i].note = -1;
+		_voices[i].hw_channel = 0;
+	}
+
+	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);
+		_channels[i].pitch = 0x2000;
+	}
+
+	Common::File file;
+
+	if (file.open("bank.001")) {
+		if (!loadInstrumentsSCI0(file)) {
+			file.close();
+			return Common::kUnknownError;
+		}
+		file.close();
+	} else {
+		ResourceManager *resMan = g_sci->getResMan();
+
+		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;
+		}
+
+		Common::MemoryReadStream stream(resource->data, resource->size);
+
+		if (_isSci1) {
+			if (!loadInstrumentsSCI1(stream))
+				return Common::kUnknownError;
+		} else if (!loadInstrumentsSCI0Mac(stream))
+			return Common::kUnknownError;
+	}
+	
+	MidiDriver_Emulated::open();
+
+	_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO);
+
+	return Common::kNoError;
+}
+
+void MidiDriver_AmigaMac::close() {
+	_mixer->stopHandle(_mixerSoundHandle);
+
+	for (uint i = 0; i < _bank.size; 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];
+			}
+		}
+	}
+}
+
+void MidiDriver_AmigaMac::playSwitch(bool play) {
+	_playSwitch = play;
+}
+
+void MidiDriver_AmigaMac::setVolume(byte volume_) {
+	_masterVolume = volume_;
+}
+
+void MidiDriver_AmigaMac::send(uint32 b) {
+	byte command = b & 0xf0;
+	byte channel = b & 0xf;
+	byte op1 = (b >> 8) & 0xff;
+	byte op2 = (b >> 16) & 0xff;
+
+	switch (command) {
+	case 0x80:
+		stopNote(channel, op1);
+		break;
+	case 0x90:
+		if (op2 > 0)
+			startNote(channel, op1, op2);
+		else
+			stopNote(channel, op1);
+		break;
+	case 0xb0:
+		switch (op1) {
+		case 0x07:
+			_channels[channel].volume = op2;
+			break;
+		case 0x0a:
+#ifdef DEBUG
+			warning("[sfx:seq:amiga] ignoring pan 0x%02x event for channel %i", op2, channel);
+#endif
+			break;
+		case 0x7b:
+			stopChannel(channel);
+			break;
+		default:
+			warning("[sfx:seq:amiga] unknown control event 0x%02x", op1);
+		}
+		break;
+	case 0xc0:
+		changeInstrument(channel, op1);
+		break;
+	case 0xe0:
+		pitchWheel(channel, (op2 << 7) | op1);
+		break;
+	default:
+		warning("[sfx:seq:amiga] unknown event %02x", command);
+	}
+}
+
+void MidiDriver_AmigaMac::generateSamples(int16 *data, int len) {
+	if (len == 0)
+		return;
+
+	int16 *buffers = (int16*)malloc(len * 2 * kChannels);
+
+	memset(buffers, 0, len * 2 * kChannels);
+
+	/* 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;
+			}
+
+			/* 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;
+
+			/* Mix */
+			for (int i = 0; i < kChannels; i++)
+				mixed += buffers[i * len + j];
+
+			/* Adjust volume */
+			data[j] = mixed * _masterVolume >> 6;
+		}
+	}
+
+	free(buffers);
+}
+
+bool MidiDriver_AmigaMac::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_AmigaMac::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);
+		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_AmigaMac::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_AmigaMac : public MidiPlayer {
+public:
+	MidiPlayer_AmigaMac(SciVersion version) : MidiPlayer(version) { _driver = new MidiDriver_AmigaMac(g_system->getMixer()); }
+	byte getPlayId();
+	int getPolyphony() const { return MidiDriver_AmigaMac::kVoices; }
+	bool hasRhythmChannel() const { return false; }
+	void setVolume(byte volume) { static_cast<MidiDriver_AmigaMac *>(_driver)->setVolume(volume); }
+	void playSwitch(bool play) { static_cast<MidiDriver_AmigaMac *>(_driver)->playSwitch(play); }
+	void loadInstrument(int idx, byte *data);
+};
+
+MidiPlayer *MidiPlayer_AmigaMac_create(SciVersion version) {
+	return new MidiPlayer_AmigaMac(version);
+}
+
+byte MidiPlayer_AmigaMac::getPlayId() {
+	if (_version > SCI_VERSION_0_LATE)
+		return 0x06;
+
+	return 0x40;
+}
+
+} // End of namespace Sci

Modified: scummvm/trunk/engines/sci/sound/drivers/mididriver.h
===================================================================
--- scummvm/trunk/engines/sci/sound/drivers/mididriver.h	2010-07-18 18:57:28 UTC (rev 51005)
+++ scummvm/trunk/engines/sci/sound/drivers/mididriver.h	2010-07-18 19:01:35 UTC (rev 51006)
@@ -113,7 +113,7 @@
 };
 
 extern MidiPlayer *MidiPlayer_AdLib_create(SciVersion version);
-extern MidiPlayer *MidiPlayer_Amiga_create(SciVersion version);
+extern MidiPlayer *MidiPlayer_AmigaMac_create(SciVersion version);
 extern MidiPlayer *MidiPlayer_PCJr_create(SciVersion version);
 extern MidiPlayer *MidiPlayer_PCSpeaker_create(SciVersion version);
 extern MidiPlayer *MidiPlayer_Midi_create(SciVersion version);

Modified: scummvm/trunk/engines/sci/sound/music.cpp
===================================================================
--- scummvm/trunk/engines/sci/sound/music.cpp	2010-07-18 18:57:28 UTC (rev 51005)
+++ scummvm/trunk/engines/sci/sound/music.cpp	2010-07-18 19:01:35 UTC (rev 51006)
@@ -71,7 +71,7 @@
 	case MT_ADLIB:
 		// FIXME: There's no Amiga sound option, so we hook it up to AdLib
 		if (g_sci->getPlatform() == Common::kPlatformAmiga || platform == Common::kPlatformMacintosh)
-			_pMidiDrv = MidiPlayer_Amiga_create(_soundVersion);
+			_pMidiDrv = MidiPlayer_AmigaMac_create(_soundVersion);
 		else
 			_pMidiDrv = MidiPlayer_AdLib_create(_soundVersion);
 		break;


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list