aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/sfx
diff options
context:
space:
mode:
authorJordi Vilalta Prat2009-02-15 06:10:59 +0000
committerJordi Vilalta Prat2009-02-15 06:10:59 +0000
commitfa6e10e9cec163845aa29e7940c86e9c9ab8a2bc (patch)
treece87338830cc8c149e1de545246bcefe4f45da00 /engines/sci/sfx
parent7c148ddf021c990fa866b7600f979aac9a5b26c9 (diff)
downloadscummvm-rg350-fa6e10e9cec163845aa29e7940c86e9c9ab8a2bc.tar.gz
scummvm-rg350-fa6e10e9cec163845aa29e7940c86e9c9ab8a2bc.tar.bz2
scummvm-rg350-fa6e10e9cec163845aa29e7940c86e9c9ab8a2bc.zip
Import the SCI engine sources from the FreeSCI Glutton branch (it doesn't compile yet)
svn-id: r38192
Diffstat (limited to 'engines/sci/sfx')
-rw-r--r--engines/sci/sfx/Makefile.am27
-rw-r--r--engines/sci/sfx/adlib.c66
-rw-r--r--engines/sci/sfx/adlib.h69
-rw-r--r--engines/sci/sfx/core.c938
-rw-r--r--engines/sci/sfx/device.h117
-rw-r--r--engines/sci/sfx/device/Makefile.am3
-rw-r--r--engines/sci/sfx/device/alsa-midi.c227
-rw-r--r--engines/sci/sfx/device/camd-midi.c161
-rw-r--r--engines/sci/sfx/device/devices.c112
-rw-r--r--engines/sci/sfx/device/unixraw-midi.c100
-rw-r--r--engines/sci/sfx/doc/README7
-rw-r--r--engines/sci/sfx/doc/patch001.txt118
-rw-r--r--engines/sci/sfx/doc/sound01.txt213
-rw-r--r--engines/sci/sfx/iterator.c2113
-rw-r--r--engines/sci/sfx/lists/GM.txt177
-rw-r--r--engines/sci/sfx/lists/gm_patches.c198
-rw-r--r--engines/sci/sfx/lists/mt32_timbres.c181
-rw-r--r--engines/sci/sfx/mixer.h119
-rw-r--r--engines/sci/sfx/mixer/Makefile.am6
-rw-r--r--engines/sci/sfx/mixer/dc.c329
-rw-r--r--engines/sci/sfx/mixer/mixers.c55
-rw-r--r--engines/sci/sfx/mixer/soft.c988
-rw-r--r--engines/sci/sfx/mixer/test.c351
-rw-r--r--engines/sci/sfx/mt32_GM_mapping/Makefile15
-rw-r--r--engines/sci/sfx/mt32_GM_mapping/README2
-rw-r--r--engines/sci/sfx/mt32_GM_mapping/gm_patches.c198
-rw-r--r--engines/sci/sfx/mt32_GM_mapping/lb2map.txt118
-rw-r--r--engines/sci/sfx/mt32_GM_mapping/main.c157
-rw-r--r--engines/sci/sfx/mt32_GM_mapping/mt32_timbres.c181
-rw-r--r--engines/sci/sfx/mt32_GM_mapping/pq1map.txt135
-rw-r--r--engines/sci/sfx/mt32_GM_mapping/qfg1map.txt123
-rw-r--r--engines/sci/sfx/old/Makefile24
-rw-r--r--engines/sci/sfx/old/README6
-rw-r--r--engines/sci/sfx/old/ROADMAP72
-rw-r--r--engines/sci/sfx/old/main.c49
-rw-r--r--engines/sci/sfx/old/midi.c30
-rw-r--r--engines/sci/sfx/old/midi.h30
-rw-r--r--engines/sci/sfx/old/midi_mt32.c341
-rw-r--r--engines/sci/sfx/old/midi_mt32.h29
-rw-r--r--engines/sci/sfx/old/midiout.c63
-rw-r--r--engines/sci/sfx/old/midiout.h29
-rw-r--r--engines/sci/sfx/old/midiout_alsaraw.c51
-rw-r--r--engines/sci/sfx/old/midiout_alsaraw.h28
-rw-r--r--engines/sci/sfx/old/midiout_unixraw.c52
-rw-r--r--engines/sci/sfx/old/midiout_unixraw.h28
-rw-r--r--engines/sci/sfx/pcm-iterator.c117
-rw-r--r--engines/sci/sfx/pcm_device/Makefile.am7
-rw-r--r--engines/sci/sfx/pcm_device/alsa.c387
-rw-r--r--engines/sci/sfx/pcm_device/audbuf_test.c190
-rw-r--r--engines/sci/sfx/pcm_device/audiobuf.c348
-rw-r--r--engines/sci/sfx/pcm_device/audiobuf.h140
-rw-r--r--engines/sci/sfx/pcm_device/pcm_devices.c73
-rw-r--r--engines/sci/sfx/pcm_device/scummvm.cpp60
-rw-r--r--engines/sci/sfx/pcm_device/sdl.c272
-rw-r--r--engines/sci/sfx/player/Makefile.am3
-rw-r--r--engines/sci/sfx/player/players.c54
-rw-r--r--engines/sci/sfx/player/polled.c335
-rw-r--r--engines/sci/sfx/player/realtime.c328
-rw-r--r--engines/sci/sfx/seq/Makefile.am5
-rw-r--r--engines/sci/sfx/seq/gm.c185
-rw-r--r--engines/sci/sfx/seq/instrument-map.c539
-rw-r--r--engines/sci/sfx/seq/instrument-map.h132
-rw-r--r--engines/sci/sfx/seq/map-mt32-to-gm.c813
-rw-r--r--engines/sci/sfx/seq/mt32.c480
-rw-r--r--engines/sci/sfx/seq/oss-adlib.c374
-rw-r--r--engines/sci/sfx/seq/sequencers.c68
-rw-r--r--engines/sci/sfx/sequencer.h142
-rw-r--r--engines/sci/sfx/softseq.h134
-rw-r--r--engines/sci/sfx/softseq/Makefile.am5
-rw-r--r--engines/sci/sfx/softseq/SN76496.c248
-rw-r--r--engines/sci/sfx/softseq/amiga.c658
-rw-r--r--engines/sci/sfx/softseq/fluidsynth.c262
-rw-r--r--engines/sci/sfx/softseq/fmopl.c1144
-rw-r--r--engines/sci/sfx/softseq/fmopl.h168
-rw-r--r--engines/sci/sfx/softseq/mt32.cpp228
-rw-r--r--engines/sci/sfx/softseq/mt32/Makefile.am5
-rw-r--r--engines/sci/sfx/softseq/mt32/freeverb.cpp310
-rw-r--r--engines/sci/sfx/softseq/mt32/freeverb.h239
-rw-r--r--engines/sci/sfx/softseq/mt32/i386.cpp849
-rw-r--r--engines/sci/sfx/softseq/mt32/i386.h49
-rw-r--r--engines/sci/sfx/softseq/mt32/mt32_file.cpp112
-rw-r--r--engines/sci/sfx/softseq/mt32/mt32_file.h67
-rw-r--r--engines/sci/sfx/softseq/mt32/mt32emu.h70
-rw-r--r--engines/sci/sfx/softseq/mt32/part.cpp632
-rw-r--r--engines/sci/sfx/softseq/mt32/part.h113
-rw-r--r--engines/sci/sfx/softseq/mt32/partial.cpp960
-rw-r--r--engines/sci/sfx/softseq/mt32/partial.h148
-rw-r--r--engines/sci/sfx/softseq/mt32/partialManager.cpp272
-rw-r--r--engines/sci/sfx/softseq/mt32/partialManager.h56
-rw-r--r--engines/sci/sfx/softseq/mt32/structures.h284
-rw-r--r--engines/sci/sfx/softseq/mt32/synth.cpp1199
-rw-r--r--engines/sci/sfx/softseq/mt32/synth.h300
-rw-r--r--engines/sci/sfx/softseq/mt32/tables.cpp749
-rw-r--r--engines/sci/sfx/softseq/mt32/tables.h116
-rw-r--r--engines/sci/sfx/softseq/opl2.c718
-rw-r--r--engines/sci/sfx/softseq/pcspeaker.c184
-rw-r--r--engines/sci/sfx/softseq/softsequencers.c71
-rw-r--r--engines/sci/sfx/songlib.c282
-rw-r--r--engines/sci/sfx/test-iterator.c450
-rw-r--r--engines/sci/sfx/tests/stubs.c9
-rw-r--r--engines/sci/sfx/tests/tests.cpp172
-rw-r--r--engines/sci/sfx/tests/tests.vcproj158
-rw-r--r--engines/sci/sfx/time.c131
-rw-r--r--engines/sci/sfx/timer/Makefile.am4
-rw-r--r--engines/sci/sfx/timer/pthread.c104
-rw-r--r--engines/sci/sfx/timer/scummvm.cpp52
-rw-r--r--engines/sci/sfx/timer/sigalrm.c157
-rw-r--r--engines/sci/sfx/timer/timers.c73
-rw-r--r--engines/sci/sfx/timetest.c59
109 files changed, 25489 insertions, 0 deletions
diff --git a/engines/sci/sfx/Makefile.am b/engines/sci/sfx/Makefile.am
new file mode 100644
index 0000000000..b085a7c917
--- /dev/null
+++ b/engines/sci/sfx/Makefile.am
@@ -0,0 +1,27 @@
+SUBDIRS = seq timer player device mixer pcm_device softseq
+INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@
+EXTRA_DIST = timetest.c adlib.h device.h mixer.h sequencer.h softseq.h
+noinst_LIBRARIES = libscisound.a libscisoundlib.a
+libscisound_a_SOURCES = iterator.c songlib.c core.c pcm-iterator.c
+libscisoundlib_a_SOURCES = time.c adlib.c
+
+bin_PROGRAMS = test-iterator
+test_iterator_SOURCE = test-iterator.c
+test_iterator_LDFLAGS = @SCIV_LDFLAGS@
+LDADD = \
+ libscisound.a \
+ player/libsciplayer.a \
+ seq/libsciseq.a \
+ timer/libscitimer.a \
+ pcm_device/libscipcm.a \
+ mixer/libscimixer.a \
+ softseq/libscisoftseq.a \
+ libscisoundlib.a \
+ device/libscisounddevice.a \
+ ../scicore/libscicore.a \
+ @LIB_M@
+
+
+TESTS = test-iterator
+
+
diff --git a/engines/sci/sfx/adlib.c b/engines/sci/sfx/adlib.c
new file mode 100644
index 0000000000..dab3902770
--- /dev/null
+++ b/engines/sci/sfx/adlib.c
@@ -0,0 +1,66 @@
+/***************************************************************************
+ adlib.c Copyright (C) 2001 Solomon Peachy
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+#include "adlib.h"
+
+adlib_instr adlib_sbi[96];
+
+void
+make_sbi(adlib_def *one, guint8 *buffer)
+{
+ memset(buffer, 0, sizeof(adlib_instr));
+
+#if 0
+ printf ("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x ", one->keyscale1, one->freqmod1, one->feedback1, one->attackrate1, one->sustainvol1, one->envelope1, one->decayrate1, one->releaserate1, one->volume1, one->ampmod1, one->vibrato1, one->keybdscale1, one->algorithm1, one->waveform1);
+
+ printf (" %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x ", one->keyscale2, one->freqmod2, one->feedback2, one->attackrate2, one->sustainvol2, one->envelope2, one->decayrate2, one->releaserate2, one->volume2, one->ampmod2, one->vibrato2, one->keybdscale2, one->algorithm2, one->waveform2);
+
+ printf("\n");
+#endif
+
+ buffer[0] |= ((one->ampmod1 & 0x1) << 7);
+ buffer[0] |= ((one->vibrato1 & 0x1) << 6);
+ buffer[0] |= ((one->envelope1 & 0x1) << 5);
+ buffer[0] |= ((one->keybdscale1 & 0x1) << 4);
+ buffer[0] |= (one->freqmod1 & 0xf);
+ buffer[1] |= ((one->ampmod2 & 0x1) << 7);
+ buffer[1] |= ((one->vibrato2 & 0x1) << 6);
+ buffer[1] |= ((one->envelope2 & 0x1) << 5);
+ buffer[1] |= ((one->keybdscale2 & 0x1) << 4);
+ buffer[1] |= (one->freqmod2 & 0xf);
+ buffer[2] |= ((one->keyscale1 & 0x3) << 6);
+ buffer[2] |= (one->volume1 & 0x3f);
+ buffer[3] |= ((one->keyscale2 & 0x3) << 6);
+ buffer[3] |= (one->volume2 & 0x3f);
+ buffer[4] |= ((one->attackrate1 & 0xf) << 4);
+ buffer[4] |= (one->decayrate1 & 0xf);
+ buffer[5] |= ((one->attackrate2 & 0xf) << 4);
+ buffer[5] |= (one->decayrate2 & 0xf);
+ buffer[6] |= ((one->sustainvol1 & 0xf) << 4);
+ buffer[6] |= (one->releaserate1 & 0xf);
+ buffer[7] |= ((one->sustainvol2 & 0xf) << 4);
+ buffer[7] |= (one->releaserate2 & 0xf);
+ buffer[8] |= (one->waveform1 & 0x3);
+ buffer[9] |= (one->waveform2 & 0x3);
+
+ buffer[10] |= ((one->feedback1 & 0x7) << 1);
+ buffer[10] |= (1-(one->algorithm1 & 0x1));
+
+ return;
+}
diff --git a/engines/sci/sfx/adlib.h b/engines/sci/sfx/adlib.h
new file mode 100644
index 0000000000..214b7100b7
--- /dev/null
+++ b/engines/sci/sfx/adlib.h
@@ -0,0 +1,69 @@
+/***************************************************************************
+ sfx_adlib.h, from
+ midi_device.h Copyright (C) 2001 Solomon Peachy
+ Copytight (C) 2002..04 Christoph Reichenbach
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+
+#ifndef _SFX_ADLIB_H_
+#define _SFX_ADLIB_H_
+
+#include <resource.h>
+
+
+#define ADLIB_VOICES 12
+
+typedef struct _sci_adlib_def {
+ guint8 keyscale1; /* 0-3 !*/
+ guint8 freqmod1; /* 0-15 !*/
+ guint8 feedback1; /* 0-7 !*/
+ guint8 attackrate1; /* 0-15 !*/
+ guint8 sustainvol1; /* 0-15 !*/
+ guint8 envelope1; /* t/f !*/
+ guint8 decayrate1; /* 0-15 !*/
+ guint8 releaserate1; /* 0-15 !*/
+ guint8 volume1; /* 0-63 !*/
+ guint8 ampmod1; /* t/f !*/
+ guint8 vibrato1; /* t/f !*/
+ guint8 keybdscale1; /* t/f !*/
+ guint8 algorithm1; /* 0,1 REVERSED */
+ guint8 keyscale2; /* 0-3 !*/
+ guint8 freqmod2; /* 0-15 !*/
+ guint8 feedback2; /* 0-7 UNUSED */
+ guint8 attackrate2; /* 0-15 !*/
+ guint8 sustainvol2; /* 0-15 !*/
+ guint8 envelope2; /* t/f !*/
+ guint8 decayrate2; /* 0-15 !*/
+ guint8 releaserate2; /* 0-15 !*/
+ guint8 volume2; /* 0-63 !*/
+ guint8 ampmod2; /* t/f !*/
+ guint8 vibrato2; /* t/f !*/
+ guint8 keybdscale2; /* t/f !*/
+ guint8 algorithm2; /* 0,1 UNUSED */
+ guint8 waveform1; /* 0-3 !*/
+ guint8 waveform2; /* 0-3 !*/
+} adlib_def;
+
+typedef unsigned char adlib_instr[12];
+
+extern adlib_instr adlib_sbi[96];
+
+void make_sbi(adlib_def *one, guint8 *buffer);
+/* Converts a raw SCI adlib instrument into the adlib register format. */
+
+#endif /* _SFX_ADLIB_H_ */
diff --git a/engines/sci/sfx/core.c b/engines/sci/sfx/core.c
new file mode 100644
index 0000000000..9d55ad6bbf
--- /dev/null
+++ b/engines/sci/sfx/core.c
@@ -0,0 +1,938 @@
+/***************************************************************************
+ core.c Copyright (C) 2002 Christoph Reichenbach
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+/* Sound subsystem core: Event handler, sound player dispatching */
+
+#include <stdio.h>
+#include <sfx_timer.h>
+#include <sfx_iterator_internal.h>
+#include <sfx_player.h>
+#include "mixer.h"
+#include <sci_midi.h>
+
+
+/*#define DEBUG_SONG_API*/
+/*#define DEBUG_CUES*/
+#ifdef DEBUG_CUES
+int sciprintf(char *msg, ...);
+#endif
+
+static sfx_player_t *player = NULL;
+sfx_pcm_mixer_t *mixer = NULL;
+static sfx_pcm_device_t *pcm_device = NULL;
+static sfx_timer_t *timer = NULL;
+
+#define MILLION 1000000
+
+int
+sfx_pcm_available()
+{
+ return (pcm_device != NULL);
+}
+
+void
+sfx_reset_player(void)
+{
+ if (player)
+ player->stop();
+}
+
+tell_synth_func *
+sfx_get_player_tell_func(void)
+{
+ if (player)
+ return player->tell_synth;
+ else
+ return NULL;
+}
+
+int
+sfx_get_player_polyphony(void)
+{
+ if (player)
+ return player->polyphony;
+ else
+ return 0;
+}
+
+static long
+time_minus(GTimeVal t1, GTimeVal t2)
+{
+ return (t1.tv_sec - t2.tv_sec) * MILLION
+ + (t1.tv_usec - t2.tv_usec);
+}
+
+static GTimeVal
+time_plus(GTimeVal t1, long delta)
+{
+ if (delta > 0)
+ t1.tv_usec += delta % MILLION;
+ else
+ t1.tv_usec -= (-delta) % MILLION;
+
+ t1.tv_sec += delta / MILLION;
+
+ if (t1.tv_usec > MILLION) {
+ t1.tv_sec++;
+ t1.tv_usec -= MILLION;
+ }
+
+ return t1;
+}
+
+
+static void
+_freeze_time(sfx_state_t *self)
+{
+ /* Freezes the top song delay time */
+ GTimeVal ctime;
+ long delta;
+
+ song_t *song = self->song;
+ sci_get_current_time(&ctime);
+
+ while (song) {
+ delta = time_minus(song->wakeup_time, ctime);
+ if (delta < 0)
+ delta = 0;
+
+ song->delay = delta;
+
+ song = song->next_playing;
+ }
+}
+
+
+static void
+_dump_playing_list(sfx_state_t *self, char *msg)
+{
+ song_t *song = self->song;
+
+ fprintf(stderr, "[] Song list : [ ");
+ song = *(self->songlib.lib);
+ while (song) {
+ fprintf(stderr, "%08lx:%d ", song->handle, song->status);
+ song = song->next_playing;
+ }
+ fprintf(stderr, "]\n");
+
+ fprintf(stderr, "[] Play list (%s) : [ " , msg);
+
+ while (song) {
+ fprintf(stderr, "%08lx ", song->handle);
+ song = song->next_playing;
+ }
+
+ fprintf(stderr, "]\n");
+}
+
+static void
+_dump_songs(sfx_state_t *self)
+{
+#if 0
+ song_t *song = self->song;
+
+ fprintf(stderr, "Cue iterators:\n");
+ song = *(self->songlib.lib);
+ while (song) {
+ fprintf(stderr, " **\tHandle %08x (p%d): status %d\n",
+ song->handle, song->priority, song->status);
+ SIMSG_SEND(song->it, SIMSG_PRINT(1));
+ song = song->next;
+ }
+
+ if (player) {
+ fprintf(stderr, "Audio iterator:\n");
+ player->iterator_message(songit_make_message(0, SIMSG_PRINT(1)));
+ }
+#endif
+}
+
+
+static void
+_thaw_time(sfx_state_t *self)
+{
+ /* inverse of _freeze_time() */
+ GTimeVal ctime;
+ song_t *song = self->song;
+
+ sci_get_current_time(&ctime);
+
+ while (song) {
+ song->wakeup_time = time_plus(ctime, song->delay);
+
+ song = song->next_playing;
+ }
+}
+
+static int
+is_playing(sfx_state_t *self, song_t *song)
+{
+ song_t *playing_song = self->song;
+
+/* _dump_playing_list(self, "is-playing");*/
+
+ while (playing_song) {
+ if (playing_song == song)
+ return 1;
+ playing_song = playing_song->next_playing;
+ }
+ return 0;
+}
+
+static void
+_sfx_set_song_status(sfx_state_t *self, song_t *song, int status)
+{
+ switch (status) {
+
+ case SOUND_STATUS_STOPPED:
+ /* Reset */
+ song->it->init(song->it);
+ break;
+
+ case SOUND_STATUS_SUSPENDED:
+ case SOUND_STATUS_WAITING:
+
+ if (song->status == SOUND_STATUS_PLAYING) {
+ /* Update delay, set wakeup_time */
+ GTimeVal time;
+ long delta;
+ sci_get_current_time(&time);
+ delta = time_minus(time, song->wakeup_time);
+
+ song->delay -= delta;
+ song->wakeup_time = time;
+ }
+ if (status == SOUND_STATUS_SUSPENDED)
+ break;
+
+ /* otherwise... */
+
+ case SOUND_STATUS_PLAYING:
+ if (song->status == SOUND_STATUS_STOPPED)
+ /* Starting anew */
+ sci_get_current_time(&song->wakeup_time);
+
+ if (is_playing(self, song))
+ status = SOUND_STATUS_PLAYING;
+ else
+ status = SOUND_STATUS_WAITING;
+ break;
+
+ default:
+ fprintf(stderr, "%s L%d: Attempt to set invalid song"
+ " state %d!\n", __FILE__, __LINE__, status);
+ return;
+
+ }
+ song->status = status;
+}
+
+/* Update internal state iff only one song may be played */
+static void
+_update_single_song(sfx_state_t *self)
+{
+ song_t *newsong = song_lib_find_active(self->songlib);
+
+ if (newsong != self->song) {
+
+ _freeze_time(self); /* Store song delay time */
+
+ if (player)
+ player->stop();
+
+ if (newsong) {
+ if (!newsong->it)
+ return; /* Restore in progress and not ready for this yet */
+
+ /* Change song */
+ if (newsong->status == SOUND_STATUS_WAITING)
+ _sfx_set_song_status(self, newsong,
+ SOUND_STATUS_PLAYING);
+
+ /* Change instrument mappings */
+ } else {
+ /* Turn off sound */
+ }
+ if (self->song) {
+ if (self->song->status == SOUND_STATUS_PLAYING)
+ _sfx_set_song_status(self, newsong,
+ SOUND_STATUS_WAITING);
+ }
+
+ if (self->debug & SFX_DEBUG_SONGS) {
+ sciprintf("[SFX] Changing active song:");
+ if (!self->song)
+ sciprintf(" New song:");
+ else
+ sciprintf(" pausing %08lx, now playing",
+ self->song->handle);
+
+ if (newsong)
+ sciprintf(" %08lx\n", newsong->handle);
+ else
+ sciprintf(" none\n");
+ }
+
+
+ self->song = newsong;
+ _thaw_time(self); /* Recover song delay time */
+
+ if (newsong && player) {
+ song_iterator_t *clonesong
+ = songit_clone(newsong->it, newsong->delay);
+
+ player->add_iterator(clonesong,
+ newsong->wakeup_time);
+ }
+ }
+}
+
+
+static void
+_update_multi_song(sfx_state_t *self)
+{
+ song_t *oldfirst = self->song;
+ song_t *oldseeker;
+ song_t *newsong = song_lib_find_active(self->songlib);
+ song_t *newseeker;
+ song_t not_playing_anymore; /* Dummy object, referenced by
+ ** songs which are no longer
+ ** active. */
+ GTimeVal tv;
+ sci_get_current_time(&tv);
+/* _dump_playing_list(self, "before");*/
+ _freeze_time(self); /* Store song delay time */
+
+ for (newseeker = newsong; newseeker;
+ newseeker = newseeker->next_playing) {
+ if (!newseeker->it)
+ return; /* Restore in progress and not ready for this yet */
+ }
+
+ /* First, put all old songs into the 'stopping' list and
+ ** mark their 'next-playing' as not_playing_anymore. */
+ for (oldseeker = oldfirst; oldseeker;
+ oldseeker = oldseeker->next_stopping) {
+ oldseeker->next_stopping = oldseeker->next_playing;
+ oldseeker->next_playing = &not_playing_anymore;
+
+ if (oldseeker == oldseeker->next_playing) { BREAKPOINT(); }
+ }
+
+ /* Second, re-generate the new song queue. */
+ for (newseeker = newsong; newseeker;
+ newseeker = newseeker->next_playing) {
+ newseeker->next_playing
+ = song_lib_find_next_active(self->songlib,
+ newseeker);
+
+ if (newseeker == newseeker->next_playing) { BREAKPOINT(); }
+ }
+ /* We now need to update the currently playing song list, because we're
+ ** going to use some functions that require this list to be in a sane
+ ** state (particularly is_playing(), indirectly */
+ self->song = newsong;
+
+ /* Third, stop all old songs */
+ for (oldseeker = oldfirst; oldseeker;
+ oldseeker = oldseeker->next_stopping)
+ if (oldseeker->next_playing == &not_playing_anymore) {
+ _sfx_set_song_status(self, oldseeker,
+ SOUND_STATUS_SUSPENDED);
+ if (self->debug & SFX_DEBUG_SONGS) {
+ sciprintf("[SFX] Stopping song %lx\n", oldseeker->handle);
+ }
+ if (player && oldseeker->it)
+ player->iterator_message
+ (songit_make_message(oldseeker->it->ID, SIMSG_STOP));
+ oldseeker->next_playing = NULL; /* Clear this pointer; we don't need the tag anymore */
+ }
+
+ for (newseeker = newsong; newseeker;
+ newseeker = newseeker->next_playing) {
+ if (newseeker->status != SOUND_STATUS_PLAYING && player) {
+ if (self->debug & SFX_DEBUG_SONGS)
+ sciprintf("[SFX] Adding song %lx\n", newseeker->it->ID);
+
+ player->add_iterator(songit_clone(newseeker->it,
+ newseeker->delay),
+ tv);
+ }
+ _sfx_set_song_status(self, newseeker,
+ SOUND_STATUS_PLAYING);
+ }
+
+ self->song = newsong;
+ _thaw_time(self);
+/* _dump_playing_list(self, "after");*/
+}
+
+/* Update internal state */
+static void
+_update(sfx_state_t *self)
+{
+ if (self->flags & SFX_STATE_FLAG_MULTIPLAY)
+ _update_multi_song(self);
+ else
+ _update_single_song(self);
+}
+
+
+static int _sfx_timer_active = 0; /* Timer toggle */
+
+int
+sfx_play_iterator_pcm(song_iterator_t *it, song_handle_t handle)
+{
+#ifdef DEBUG_SONG_API
+ fprintf(stderr, "[sfx-core] Playing PCM: %08lx\n", handle);
+#endif
+ if (mixer) {
+ sfx_pcm_feed_t *newfeed = it->get_pcm_feed(it);
+ if (newfeed) {
+ newfeed->debug_nr = (int) handle;
+ mixer->subscribe(mixer, newfeed);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void
+_sfx_timer_callback(void *data)
+{
+ if (_sfx_timer_active) {
+ /* First run the player, to give it a chance to fill
+ ** the audio buffer */
+
+ if (player)
+ player->maintenance();
+
+ if (mixer)
+ mixer->process(mixer);
+ }
+}
+
+void
+sfx_init(sfx_state_t *self, resource_mgr_t *resmgr, int flags)
+{
+ song_lib_init(&self->songlib);
+ self->song = NULL;
+ self->flags = flags;
+ self->debug = 0; /* Disable all debugging by default */
+
+ if (flags & SFX_STATE_FLAG_NOSOUND) {
+ mixer = NULL;
+ pcm_device = NULL;
+ player = NULL;
+ sciprintf("[SFX] Sound disabled.\n");
+ return;
+ }
+
+ mixer = sfx_pcm_find_mixer(NULL);
+ pcm_device = sfx_pcm_find_device(NULL);
+ player = sfx_find_player(NULL);
+
+
+#ifdef DEBUG_SONG_API
+ fprintf(stderr, "[sfx-core] Initialising: flags=%x\n", flags);
+#endif
+
+ /*------------------*/
+ /* Initialise timer */
+ /*------------------*/
+
+ if (pcm_device || player->maintenance) {
+ if (pcm_device && pcm_device->timer)
+ timer = pcm_device->timer;
+ else
+ timer = sfx_find_timer(NULL);
+
+ if (!timer) {
+ fprintf(stderr, "[SFX] " __FILE__": Could not find timing mechanism\n");
+ fprintf(stderr, "[SFX] Disabled sound support\n");
+ pcm_device = NULL;
+ player = NULL;
+ mixer = NULL;
+ return;
+ }
+
+ if (timer->init(_sfx_timer_callback, NULL)) {
+ fprintf(stderr, "[SFX] " __FILE__": Timer failed to initialize\n");
+ fprintf(stderr, "[SFX] Disabled sound support\n");
+ timer = NULL;
+ pcm_device = NULL;
+ player = NULL;
+ mixer = NULL;
+ return;
+ }
+
+ sciprintf("[SFX] Initialised timer '%s', v%s\n",
+ timer->name, timer->version);
+ } /* With no PCM device and no player, we don't need a timer */
+
+ /*----------------*/
+ /* Initialise PCM */
+ /*----------------*/
+
+ if (!pcm_device) {
+ sciprintf("[SFX] No PCM device found, disabling PCM support\n");
+ mixer = NULL;
+ } else {
+ if (pcm_device->init(pcm_device)) {
+ sciprintf("[SFX] Failed to open PCM device, disabling PCM support\n");
+ mixer = NULL;
+ pcm_device = NULL;
+ } else {
+ if (mixer->init(mixer, pcm_device)) {
+ sciprintf("[SFX] Failed to initialise PCM mixer; disabling PCM support\n");
+ mixer = NULL;
+ pcm_device->exit(pcm_device);
+ pcm_device = NULL;
+ }
+ }
+ }
+
+ /*-------------------*/
+ /* Initialise player */
+ /*-------------------*/
+
+ if (!resmgr) {
+ sciprintf("[SFX] Warning: No resource manager present, cannot initialise player\n");
+ player = NULL;
+ } else if (player->init(resmgr, timer? timer->delay_ms : 0)) {
+ sciprintf("[SFX] Song player '%s' reported error, disabled\n", player->name);
+ player = NULL;
+ }
+
+ if (!player)
+ sciprintf("[SFX] No song player found\n");
+ else
+ sciprintf("[SFX] Using song player '%s', v%s\n", player->name, player->version);
+
+ _sfx_timer_active = 1;
+}
+
+void
+sfx_exit(sfx_state_t *self)
+{
+ _sfx_timer_active = 0;
+#ifdef DEBUG_SONG_API
+ fprintf(stderr, "[sfx-core] Uninitialising\n");
+#endif
+
+ song_lib_free(self->songlib);
+
+ if (pcm_device) {
+ pcm_device->exit(pcm_device);
+ pcm_device = NULL;
+ }
+ if (timer && timer->exit())
+ fprintf(stderr, "[SFX] Timer reported error on exit\n");
+
+ /* WARNING: The mixer may hold feeds from the
+ ** player, so we must stop the mixer BEFORE
+ ** stopping the player. */
+ if (mixer) {
+ mixer->exit(mixer);
+ mixer = NULL;
+ }
+
+ if (player)
+ /* See above: This must happen AFTER stopping the mixer */
+ player->exit();
+
+}
+
+static inline int
+time_le(GTimeVal a, GTimeVal b)
+{
+ return time_minus(a, b) <= 0;
+}
+
+void
+sfx_suspend(sfx_state_t *self, int suspend)
+{
+#ifdef DEBUG_SONG_API
+ fprintf(stderr, "[sfx-core] Suspending? = %d\n", suspend);
+#endif
+ if (suspend && (!self->suspended)) {
+ /* suspend */
+
+ _freeze_time(self);
+ if (player)
+ player->pause();
+ /* Suspend song player */
+
+ } else if (!suspend && (self->suspended)) {
+ /* unsuspend */
+
+ _thaw_time(self);
+ if (player)
+ player->resume();
+
+ /* Unsuspend song player */
+ }
+
+ self->suspended = suspend;
+}
+
+int
+sfx_poll(sfx_state_t *self, song_handle_t *handle, int *cue)
+/* Polls the sound server for cues etc.
+** Returns : (int) 0 if the cue queue is empty, SI_LOOP, SI_CUE, or SI_FINISHED otherwise
+** (song_handle_t) *handle: The affected handle
+** (int) *cue: The sound cue number (if SI_CUE)
+*/
+{
+ if (!self->song)
+ return 0; /* No milk today */
+
+ *handle = self->song->handle;
+
+#ifdef DEBUG_SONG_API
+ fprintf(stderr, "[sfx-core] Polling any (%08lx)\n", *handle);
+#endif
+ return sfx_poll_specific(self, *handle, cue);
+}
+
+int
+sfx_poll_specific(sfx_state_t *self, song_handle_t handle, int *cue)
+{
+ GTimeVal ctime;
+ song_t *song = self->song;
+
+ sci_get_current_time(&ctime);
+
+ while (song && song->handle != handle)
+ song = song->next_playing;
+
+ if (!song)
+ return 0; /* Song not playing */
+
+ if (self->debug & SFX_DEBUG_CUES) {
+ fprintf(stderr, "[SFX:CUE] Polled song %08lx ", handle);
+ }
+
+ while (1) {
+ unsigned char buf[8];
+ int result;
+
+ if (!time_le(song->wakeup_time, ctime))
+ return 0; /* Patience, young hacker! */
+ result = songit_next(&(song->it), buf, cue,
+ IT_READER_MASK_ALL);
+
+ switch (result) {
+
+ case SI_FINISHED:
+ _sfx_set_song_status(self, song,
+ SOUND_STATUS_STOPPED);
+ _update(self);
+ /* ...fall through... */
+ case SI_LOOP:
+ case SI_RELATIVE_CUE:
+ case SI_ABSOLUTE_CUE:
+ if (self->debug & SFX_DEBUG_CUES) {
+ sciprintf(" => ");
+
+ if (result == SI_FINISHED)
+ sciprintf("finished\n");
+ else {
+ if (result == SI_LOOP)
+ sciprintf("Loop: ");
+ else
+ sciprintf("Cue: ");
+
+ sciprintf("%d (0x%x)", *cue, *cue);
+ }
+ }
+ return result;
+
+ default:
+ if (result > 0)
+ song->wakeup_time =
+ time_plus(song->wakeup_time,
+ result * SOUND_TICK);
+ /* Delay */
+ break;
+ }
+ }
+ if (self->debug & SFX_DEBUG_CUES) {
+ fprintf(stderr, "\n");
+ }
+}
+
+
+/*****************/
+/* Song basics */
+/*****************/
+
+int
+sfx_add_song(sfx_state_t *self, song_iterator_t *it, int priority, song_handle_t handle, int number)
+{
+ song_t *song = song_lib_find(self->songlib, handle);
+
+#ifdef DEBUG_SONG_API
+ fprintf(stderr, "[sfx-core] Adding song: %08lx at %d, it=%p\n", handle, priority, it);
+#endif
+ if (!it) {
+ fprintf(stderr, "[SFX] Attempt to add empty song with handle %08lx\n", handle);
+ return -1;
+ }
+
+ it->init(it);
+
+ /* If we're already playing this, stop it */
+ /* Tell player to shut up */
+ _dump_songs(self);
+
+ if (player)
+ player->iterator_message(songit_make_message(handle, SIMSG_STOP));
+
+ if (song) {
+ _sfx_set_song_status(self, song, SOUND_STATUS_STOPPED);
+
+ fprintf(stderr, "Overwriting old song (%08lx) ...\n", handle);
+ if (song->status == SOUND_STATUS_PLAYING
+ || song->status == SOUND_STATUS_SUSPENDED) {
+ fprintf(stderr, "Unexpected (error): Song %ld still playing/suspended (%d)\n",
+ handle, song->status);
+ songit_free(it);
+ return -1;
+ } else
+ song_lib_remove(self->songlib, handle); /* No duplicates */
+
+ }
+
+ song = song_new(handle, it, priority);
+ song->resource_num = number;
+ song->hold = 0;
+ song->loops = 0;
+ sci_get_current_time(&song->wakeup_time); /* No need to delay */
+ song_lib_add(self->songlib, song);
+ self->song = NULL; /* As above */
+ _update(self);
+
+ return 0;
+}
+
+
+void
+sfx_remove_song(sfx_state_t *self, song_handle_t handle)
+{
+#ifdef DEBUG_SONG_API
+ fprintf(stderr, "[sfx-core] Removing song: %08lx\n", handle);
+#endif
+ if (self->song && self->song->handle == handle)
+ self->song = NULL;
+
+ song_lib_remove(self->songlib, handle);
+ _update(self);
+}
+
+
+
+/**********************/
+/* Song modifications */
+/**********************/
+
+#define ASSERT_SONG(s) if (!(s)) { fprintf(stderr, "Looking up song handle %08lx failed in %s, L%d\n", handle, __FILE__, __LINE__); return; }
+
+void
+sfx_song_set_status(sfx_state_t *self, song_handle_t handle, int status)
+{
+ song_t *song = song_lib_find(self->songlib, handle);
+ ASSERT_SONG(song);
+#ifdef DEBUG_SONG_API
+ fprintf(stderr, "[sfx-core] Setting song status to %d"
+ " (0:stop, 1:play, 2:susp, 3:wait): %08lx\n", status, handle);
+#endif
+
+ _sfx_set_song_status(self, song, status);
+
+ _update(self);
+}
+
+void
+sfx_song_set_fade(sfx_state_t *self, song_handle_t handle,
+ fade_params_t *params)
+{
+#ifdef DEBUG_SONG_API
+ static const char *stopmsg[] = {"??? Should not happen", "Do not stop afterwards","Stop afterwards"};
+#endif
+ song_t *song = song_lib_find(self->songlib, handle);
+
+ ASSERT_SONG(song);
+
+#ifdef DEBUG_SONG_API
+ fprintf(stderr, "[sfx-core] Setting fade params of %08lx to "
+ "final volume %d in steps of %d per %d ticks. %s.\n",
+ handle, fade->final_volume, fade->step_size, fade->ticks_per_step,
+ stopmsg[fade->action]);
+#endif
+
+ SIMSG_SEND_FADE(song->it, params);
+
+ _update(self);
+}
+
+void
+sfx_song_renice(sfx_state_t *self, song_handle_t handle, int priority)
+{
+ song_t *song = song_lib_find(self->songlib, handle);
+ ASSERT_SONG(song);
+#ifdef DEBUG_SONG_API
+ fprintf(stderr, "[sfx-core] Renicing song %08lx to %d\n",
+ handle, priority);
+#endif
+
+ song->priority = priority;
+
+ _update(self);
+}
+
+void
+sfx_song_set_loops(sfx_state_t *self, song_handle_t handle, int loops)
+{
+ song_t *song = song_lib_find(self->songlib, handle);
+ song_iterator_message_t msg
+ = songit_make_message(handle, SIMSG_SET_LOOPS(loops));
+ ASSERT_SONG(song);
+
+#ifdef DEBUG_SONG_API
+ fprintf(stderr, "[sfx-core] Setting loops on %08lx to %d\n",
+ handle, loops);
+#endif
+ songit_handle_message(&(song->it), msg);
+ song->loops = ((base_song_iterator_t *) song->it)->loops;
+
+ if (player/* && player->send_iterator_message*/)
+ /* FIXME: The above should be optional! */
+ player->iterator_message(msg);
+}
+
+void
+sfx_song_set_hold(sfx_state_t *self, song_handle_t handle, int hold)
+{
+ song_t *song = song_lib_find(self->songlib, handle);
+ song_iterator_message_t msg
+ = songit_make_message(handle, SIMSG_SET_HOLD(hold));
+ ASSERT_SONG(song);
+
+ song->hold = hold;
+#ifdef DEBUG_SONG_API
+ fprintf(stderr, "[sfx-core] Setting hold on %08lx to %d\n",
+ handle, loops);
+#endif
+ songit_handle_message(&(song->it), msg);
+
+ if (player/* && player->send_iterator_message*/)
+ /* FIXME: The above should be optional! */
+ player->iterator_message(msg);
+}
+
+/* Different from the one in iterator.c */
+static const int MIDI_cmdlen[16] = {0, 0, 0, 0, 0, 0, 0, 0,
+ 3, 3, 0, 3, 2, 0, 3, 0};
+
+static const song_handle_t midi_send_base = 0xffff0000;
+
+static song_handle_t midi_send_handle = 0xffff0000;
+
+int
+sfx_send_midi(sfx_state_t *self, song_handle_t handle, int channel,
+ int command, int arg1, int arg2)
+{
+ byte buffer[5];
+ tell_synth_func *tell = sfx_get_player_tell_func();
+
+ /* Yes, in that order. SCI channel mutes are actually done via
+ a counting semaphore. 0 means to decrement the counter, 1
+ to increment it. */
+ static char *channel_state[] = {"ON","OFF"};
+
+ if (command == 0xb0 &&
+ arg1 == SCI_MIDI_CHANNEL_MUTE)
+ {
+ sciprintf("TODO: channel mute (channel %d %s)!\n", channel,
+ channel_state[arg2]);
+ /* We need to have a GET_PLAYMASK interface to use
+ here. SET_PLAYMASK we've got.
+ */
+ return SFX_OK;
+ }
+
+ buffer[0] = channel | command; /* No channel remapping yet */
+
+ switch (command)
+ {
+ case 0x80 :
+ case 0x90 :
+ case 0xb0 :
+ buffer[1] = arg1&0xff;
+ buffer[2] = arg2&0xff;
+ break;
+ case 0xc0 :
+ buffer[1] = arg1&0xff;
+ break;
+ case 0xe0 :
+ buffer[1] = (arg1&0x7f) | 0x80;
+ buffer[2] = (arg1&0xff00) >> 7;
+ break;
+ default:
+ sciprintf("Unexpected explicit MIDI command %02x\n", command);
+ return SFX_ERROR;
+ }
+
+ if (tell)
+ tell(MIDI_cmdlen[command >> 4], buffer);
+ return SFX_OK;
+}
+
+int
+sfx_get_volume(sfx_state_t *self)
+{
+ fprintf(stderr, "FIXME: Implement volume\n");
+ return 0;
+}
+
+void
+sfx_set_volume(sfx_state_t *self, int volume)
+{
+ fprintf(stderr, "FIXME: Implement volume\n");
+}
+
+void
+sfx_all_stop(sfx_state_t *self)
+{
+#ifdef DEBUG_SONG_API
+ fprintf(stderr, "[sfx-core] All stop\n");
+#endif
+
+ song_lib_free(self->songlib);
+ _update(self);
+}
diff --git a/engines/sci/sfx/device.h b/engines/sci/sfx/device.h
new file mode 100644
index 0000000000..49bc017c7b
--- /dev/null
+++ b/engines/sci/sfx/device.h
@@ -0,0 +1,117 @@
+/***************************************************************************
+ sfx_device.h Copyright (C) 2003,04 Christoph Reichenbach
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+/* song player structure */
+
+#ifndef _SFX_DEVICE_H
+#define _SFX_DEVICE_H
+
+/* Various types of resources */
+#define SFX_DEVICE_NONE 0
+#define SFX_DEVICE_MIDI 1 /* midi writer */
+#define SFX_DEVICE_OPL2 2 /* OPL/2 sequencer */
+
+struct _midi_device {
+ const char *name;
+
+ int (*init)(struct _midi_device *self);
+ /* Initializes the device
+ ** Parameters: (midi_device_t *) self: Self reference
+ ** Returns : (int) SFX_OK on success, SFX_ERROR if the device could not be
+ ** opened
+ */
+
+ int (*set_option)(struct _midi_device *self, char *name, char *value);
+ /* Sets an option for the device
+ ** Parameters: (char *) name: Name of the option to set
+ ** (char *) value: Value of the option to set
+ ** Returns : (int) SFX_OK on success, SFX_ERROR otherwise (unsupported option)
+ */
+};
+
+#define MIDI_WRITER_BODY \
+ char *name; /* Name description of the device */ \
+ \
+ int (*init)(struct _midi_writer *self); \
+ /* Initializes the writer \
+ ** Parameters: (midi_writer_t *) self: Self reference \
+ ** Returns : (int) SFX_OK on success, SFX_ERROR if the device could not be \
+ ** opened \
+ */ \
+ \
+ int (*set_option)(struct _midi_writer *self, char *name, char *value); \
+ /* Sets an option for the writer \
+ ** Parameters: (char *) name: Name of the option to set \
+ ** (char *) value: Value of the option to set \
+ ** Returns : (int) SFX_OK on success, SFX_ERROR otherwise (unsupported option) \
+ */ \
+ \
+ int (*write)(struct _midi_writer *self, unsigned char *buf, int len); \
+ /* Writes some bytes to the MIDI stream \
+ ** Parameters: (char *) buf: The buffer to write \
+ ** (int) len: Number of bytes to write \
+ ** Returns : (int) SFX_OK on success, SFX_ERROR on failure \
+ ** No delta time is expected here. \
+ */ \
+ \
+ void (*delay)(struct _midi_writer *self, int ticks); \
+ /* Introduces an explicit delay \
+ ** Parameters: (int) ticks: Number of 60 Hz ticks to sleep \
+ */ \
+ \
+ void (*flush)(struct _midi_writer *self); /* May be NULL */ \
+ /* Flushes the MIDI file descriptor \
+ ** Parameters: (midi_writer_t *) self: Self reference \
+ */ \
+ \
+ void (*reset_timer)(struct _midi_writer *self); \
+ /* Resets the timer associated with this device \
+ ** Parameters: (midi_writer_t *) self: Self reference \
+ ** This function makes sure that a subsequent write would have effect \
+ ** immediately, and any delay() would be relative to the point in time \
+ ** this function was invoked at. \
+ */ \
+ \
+ void (*close)(struct _midi_writer *self); \
+ /* Closes the associated MIDI device \
+ ** Parameters: (midi_writer_t *) self: Self reference \
+ */
+
+typedef struct _midi_writer {
+ MIDI_WRITER_BODY
+} midi_writer_t;
+
+
+void *
+sfx_find_device(int type, char *name);
+/* Looks up a device by name
+** Parameters: (int) type: Device type to look up
+** (char *) name: Comma-separated list of devices to choose from
+** (in the order specified), or NULL for default
+** Returns : (void *) The device requested, or NULL if no match was found
+*/
+
+#endif /* !_SFX_PLAYER_H */
diff --git a/engines/sci/sfx/device/Makefile.am b/engines/sci/sfx/device/Makefile.am
new file mode 100644
index 0000000000..4a4f023251
--- /dev/null
+++ b/engines/sci/sfx/device/Makefile.am
@@ -0,0 +1,3 @@
+noinst_LIBRARIES = libscisounddevice.a
+INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@
+libscisounddevice_a_SOURCES = devices.c alsa-midi.c unixraw-midi.c camd-midi.c
diff --git a/engines/sci/sfx/device/alsa-midi.c b/engines/sci/sfx/device/alsa-midi.c
new file mode 100644
index 0000000000..67ca995916
--- /dev/null
+++ b/engines/sci/sfx/device/alsa-midi.c
@@ -0,0 +1,227 @@
+/***************************************************************************
+ alsa-midi.c Copyright (C) 2002 Christoph Reichenbach
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+
+#include <sfx_engine.h>
+#include "../device.h"
+#ifdef HAVE_ALSA
+
+#include <alsa/asoundlib.h>
+
+#define SCI_ALSA_MIDI_VERSION "0.1"
+
+static snd_midi_event_t *parser = NULL;
+static snd_seq_t *seq = NULL;
+static int queue = -1;
+static int delta = 0;
+static int port_out = -1;
+static int port_nr = 128;
+static int subport_nr = 0;
+
+static const char *seq_name = "default";
+
+static void
+_set_tempo(void)
+{
+ int resolution = 60;
+ int tempo = 1;
+ snd_seq_queue_tempo_t *queue_tempo;
+
+ snd_seq_queue_tempo_malloc(&queue_tempo);
+
+ memset(queue_tempo, 0, snd_seq_queue_tempo_sizeof());
+ snd_seq_queue_tempo_set_ppq(queue_tempo, resolution);
+ snd_seq_queue_tempo_set_tempo(queue_tempo, 1000000/tempo);
+
+ snd_seq_set_queue_tempo(seq, queue, queue_tempo);
+
+ snd_seq_queue_tempo_free(queue_tempo);
+
+#if 0
+ int tempo = 1000000 / 60;
+ snd_seq_queue_tempo_t *queue_tempo;
+
+ snd_seq_queue_tempo_malloc(&queue_tempo);
+ snd_seq_queue_tempo_set_tempo(queue_tempo, tempo);
+ snd_seq_queue_tempo_set_ppq(queue_tempo, 1);
+ snd_seq_set_queue_tempo(seq, queue, queue_tempo);
+ snd_seq_queue_tempo_free(queue_tempo);
+#endif
+}
+
+
+static int
+am_subscribe_to_ports(void)
+{
+ if ((port_out = snd_seq_connect_to(seq, port_out, port_nr, subport_nr)) < 0) {
+ fprintf(stderr, "[SFX] Could not connect to ALSA sequencer port: %s\n", snd_strerror(port_out));
+ return SFX_ERROR;
+ }
+ return SFX_OK;
+}
+
+
+static int
+aminit(midi_writer_t *self)
+{
+ int err;
+
+ snd_midi_event_new(4096, &parser);
+ snd_midi_event_init(parser);
+
+ sciprintf("[SFX] Initialising ALSA MIDI backend, v%s\n", SCI_ALSA_MIDI_VERSION);
+
+ if (snd_seq_open(&seq, seq_name, SND_SEQ_OPEN_OUTPUT, SND_SEQ_NONBLOCK)) {
+ fprintf(stderr, "[SFX] Failed to open ALSA MIDI sequencer '%s' for output\n",
+ seq_name);
+ return SFX_ERROR;
+ }
+
+ if ((port_out = snd_seq_create_simple_port(seq, "FreeSCI",
+ SND_SEQ_PORT_CAP_WRITE |
+ SND_SEQ_PORT_CAP_SUBS_WRITE |
+ SND_SEQ_PORT_CAP_READ,
+ SND_SEQ_PORT_TYPE_MIDI_GENERIC)) < 0) {
+ fprintf(stderr, "[SFX] Could not create ALSA sequencer port\n");
+ return SFX_ERROR;
+ }
+
+ if (am_subscribe_to_ports())
+ return SFX_ERROR;
+
+ queue = snd_seq_alloc_queue(seq);
+ _set_tempo();
+
+ snd_seq_start_queue(seq, queue, NULL);
+
+ if ((err = snd_seq_drain_output(seq))) {
+ fflush(NULL);
+ fprintf(stderr, "[SFX] Error while draining: %s\n",
+ snd_strerror(err));
+ return SFX_ERROR;
+ }
+
+ return SFX_OK;
+}
+
+static int
+amsetopt(midi_writer_t *self, char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+
+static int
+amwrite(midi_writer_t *self, unsigned char *buf, int len)
+{
+ snd_seq_event_t evt;
+
+#if 0
+ {
+ int i;
+ fprintf(stderr, "[MID] ");
+ for (i = 0; i < len; i++)
+ fprintf(stderr, " %02x", buf[i]);
+ fprintf(stderr, "\n");
+ }
+#endif
+
+ snd_seq_ev_clear(&evt);
+ snd_seq_ev_set_source(&evt, port_out);
+ snd_seq_ev_set_subs(&evt); /* Broadcast to all subscribers */
+
+ snd_midi_event_encode(parser, buf, len, &evt);
+ snd_seq_ev_schedule_tick(&evt, queue, 0, delta);
+
+ snd_seq_event_output_direct(seq, &evt);
+
+#if 0
+ {
+ snd_seq_queue_status_t *status;
+ snd_seq_queue_status_malloc(&status);
+
+ snd_seq_get_queue_status(seq, queue, status);
+ //snd_seq_tick_time_t snd_seq_queue_status_get_tick_time(const snd_seq_queue_status_t *info);
+ fprintf(stderr, "Queue at %d/%d\n", delta, snd_seq_queue_status_get_tick_time(status));
+
+ snd_seq_queue_status_free(status);
+ }
+#endif
+
+
+ return SFX_OK;
+}
+
+static void
+amdelay(midi_writer_t *self, int ticks)
+{
+ delta += ticks;
+}
+
+static void
+amreset_timer(midi_writer_t *self)
+{
+ snd_seq_drain_output(seq);
+ snd_seq_stop_queue(seq, queue, NULL);
+
+
+ {
+ snd_seq_event_t evt;
+ snd_seq_ev_clear(&evt);
+ snd_seq_ev_set_source(&evt, port_out);
+ snd_seq_ev_set_subs(&evt); /* Broadcast to all subscribers */
+
+ snd_seq_ev_set_queue_pos_tick(&evt, queue, 0);
+
+ snd_seq_event_output_direct(seq, &evt);
+ }
+ delta = 0;
+
+
+
+ snd_seq_start_queue(seq, queue, NULL);
+}
+
+static void
+amclose(midi_writer_t *self)
+{
+ snd_midi_event_free(parser);
+ parser = NULL;
+}
+
+
+midi_writer_t sfx_device_midi_alsa = {
+ "alsa",
+ aminit,
+ amsetopt,
+ amwrite,
+ amdelay,
+ NULL,
+ amreset_timer,
+ amclose,
+};
+
+#endif
diff --git a/engines/sci/sfx/device/camd-midi.c b/engines/sci/sfx/device/camd-midi.c
new file mode 100644
index 0000000000..3af12f196e
--- /dev/null
+++ b/engines/sci/sfx/device/camd-midi.c
@@ -0,0 +1,161 @@
+/***************************************************************************
+ camd-midi.c Copyright (C) 2005--08 Walter van Niftrik, Christoph Reichenbach
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Walter van Niftrik <w.f.b.w.v.niftrik@stud.tue.nl>
+
+***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#ifdef HAVE_PROTO_CAMD_H
+
+#include "sfx_engine.h"
+#include "../device.h"
+
+#include <proto/camd.h>
+#include <proto/dos.h>
+#include <proto/exec.h>
+#include <proto/intuition.h>
+#include <stdio.h>
+
+
+#define SWAP_BYTES
+#define FILL_BYTES
+
+#define SCI_CAMD_MIDI_VERSION "0.1"
+#define SYSEX_PREFIX 0xf0
+
+static const char *devicename = "via686.out.0";
+
+struct Library *CamdBase = NULL;
+struct CamdIFace *ICamd = NULL;
+static struct MidiLink *midi_link = NULL;
+static struct MidiNode *midi_node = NULL;
+
+#define ABORT(m) { \
+ if (CamdBase) \
+ IExec->CloseLibrary(CamdBase); \
+ sciprintf("[SFX] CAMD driver: "); \
+ sciprintf(m); \
+ sciprintf("\n"); \
+ return SFX_ERROR; \
+ }
+
+static int
+camd_init(midi_writer_t *self)
+{
+ sciprintf("[SFX] Initialising CAMD raw MIDI backend, v%s\n", SCI_CAMD_MIDI_VERSION);
+
+ CamdBase = IExec->OpenLibrary("camd.library", 36L);
+ if (!CamdBase)
+ ABORT("Could not open 'camd.library'");
+
+ ICamd = (struct CamdIFace *) IExec->GetInterface(CamdBase, "main", 1, NULL);
+ if (!ICamd)
+ ABORT("Error while retrieving CAMD interface\n");
+
+ midi_node = ICamd->CreateMidi(MIDI_MsgQueue, 0L, MIDI_SysExSize, 4096L, MIDI_Name, "freesci", TAG_END);
+ if (!midi_node)
+ ABORT("Could not create CAMD MIDI node");
+
+ midi_link = ICamd->AddMidiLink(midi_node, MLTYPE_Sender, MLINK_Location, devicename, TAG_END);
+ if (!midi_link)
+ ABORT(("Could not create CAMD MIDI link to '%s'", devicename));
+
+ sciprintf("[SFX] CAMD initialisation completed\n");
+
+ return SFX_OK;
+}
+
+static int
+camd_set_option(midi_writer_t *self, char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+#define MAX_MIDI_LEN 3
+
+static int
+camd_write(midi_writer_t *self, unsigned char *buffer, int len)
+{
+ if (len == 0)
+ return SFX_OK;
+
+ if (buffer[0] == SYSEX_PREFIX) {
+ /* Must send this as a SysEx */
+ ICamd->PutSysEx(midi_link, buffer);
+ } else {
+ ULONG data = 0l;
+ int i;
+ int readlen = (len > MAX_MIDI_LEN) ? MAX_MIDI_LEN : len;
+
+ for (i = 0; i < readlen; i++)
+ if (len >= i) {
+ data <<= 8;
+ data |= buffer[i];
+ }
+ data <<= (8 * (sizeof(ULONG) - readlen));
+
+ if (len > MAX_MIDI_LEN)
+ sciprintf("[SFX] Warning: Truncated MIDI message to fit CAMD format (sent %d: %02x %02x %02x, real length %d)\n",
+ MAX_MIDI_LEN, buffer[0], buffer[1], buffer[2], len);
+
+ ICamd->PutMidi(midi_link, data);
+ }
+
+ return SFX_OK;
+}
+
+static void
+camd_delay(midi_writer_t *self, int ticks)
+{
+}
+
+static void
+camd_reset_timer(midi_writer_t *self)
+{
+}
+
+static void
+camd_close(midi_writer_t *self)
+{
+#ifdef NO_OP
+ return;
+#endif
+ if (CamdBase)
+ IExec->CloseLibrary(CamdBase);
+}
+
+midi_writer_t sfx_device_midi_camd = {
+ "camd-midi",
+ &camd_init,
+ &camd_set_option,
+ &camd_write,
+ &camd_delay,
+ NULL,
+ &camd_reset_timer,
+ &camd_close
+};
+
+#endif /* HAVE_PROTO_CAMD_H */
diff --git a/engines/sci/sfx/device/devices.c b/engines/sci/sfx/device/devices.c
new file mode 100644
index 0000000000..78d8c4279c
--- /dev/null
+++ b/engines/sci/sfx/device/devices.c
@@ -0,0 +1,112 @@
+/***************************************************************************
+ devices.c Copyright (C) 2002 Christoph Reichenbach
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include "../device.h"
+#include <stdio.h>
+
+#ifndef SCUMMVM
+#ifdef HAVE_ALSA
+extern struct _midi_device sfx_device_midi_alsa;
+#endif
+#if !defined(_DOS) && !defined(_WIN32) && !defined(_DREAMCAST) && !defined(__MORPHOS__) && !defined(ARM_WINCE) && !defined(_GP32)
+extern struct _midi_device sfx_device_midi_unixraw;
+#endif
+
+#ifdef HAVE_PROTO_CAMD_H
+extern struct _midi_device sfx_device_midi_camd;
+#endif
+#endif // SCUMMVM
+
+#include <resource.h>
+
+static struct _midi_device *devices_midi[] = {
+#ifndef SCUMMVM
+#ifdef HAVE_PROTO_CAMD_H
+ &sfx_device_midi_camd,
+#endif
+#ifdef HAVE_ALSA
+ &sfx_device_midi_alsa,
+#endif
+#if !defined(_DOS) && !defined(_WIN32) && !defined(_DREAMCAST) && !defined(__MORPHOS__) && !defined(ARM_WINCE) && !defined(_GP32)
+ &sfx_device_midi_unixraw,
+#endif
+#endif // SCUMMVM
+ NULL
+};
+
+static struct _midi_device *devices_opl2[] = {
+ NULL
+};
+
+
+/** -- **/
+
+struct _midi_device **devices[] = {
+ NULL, /* No device */
+ devices_midi,
+ devices_opl2,
+};
+
+
+static struct _midi_device *
+find_dev(int type, char *name)
+{
+ int i = 0;
+
+ if (!type)
+ return NULL;
+
+ if (!name)
+ return devices[type][0];
+
+ while (devices[type][i] && !strcmp(name, devices[type][i]->name))
+ ++i;
+
+ return devices[type][i];
+}
+
+
+void *
+sfx_find_device(int type, char *name)
+{
+ struct _midi_device *dev = find_dev(type, name);
+
+ if (dev) {
+ if (dev->init(dev)) {
+ fprintf(stderr, "[SFX] Opening device '%s' failed\n",
+ dev->name);
+ return NULL;
+ }
+
+ return dev;
+ };
+
+ return NULL;
+}
diff --git a/engines/sci/sfx/device/unixraw-midi.c b/engines/sci/sfx/device/unixraw-midi.c
new file mode 100644
index 0000000000..69ce3890fc
--- /dev/null
+++ b/engines/sci/sfx/device/unixraw-midi.c
@@ -0,0 +1,100 @@
+/***************************************************************************
+ unixraw-midi.c Copyright (C) 2005 Walter van Niftrik
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Walter van Niftrik <w.f.b.w.v.niftrik@stud.tue.nl>
+
+***************************************************************************/
+
+#include <sfx_engine.h>
+#include "../device.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define SCI_UNIXRAW_MIDI_VERSION "0.1"
+
+#ifndef O_SYNC
+# define O_SYNC 0
+#endif
+
+static int fd;
+static const char *devicename = "/dev/midi";
+
+static int
+unixraw_init(midi_writer_t *self)
+{
+ sciprintf("[SFX] Initialising UNIX raw MIDI backend, v%s\n", SCI_UNIXRAW_MIDI_VERSION);
+
+ fd = open(devicename, O_WRONLY|O_SYNC);
+
+ if (!IS_VALID_FD(fd)) {
+ sciprintf("[SFX] Failed to open %s\n", devicename);
+ return SFX_ERROR;
+ }
+
+ return SFX_OK;
+}
+
+static int
+unixraw_set_option(midi_writer_t *self, char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+static int
+unixraw_write(midi_writer_t *self, unsigned char *buffer, int len)
+{
+ if (write(fd, buffer, len) != len) {
+ sciprintf("[SFX] MIDI write error\n");
+ return SFX_ERROR;
+ }
+ return SFX_OK;
+}
+
+static void
+unixraw_delay(midi_writer_t *self, int ticks)
+{
+}
+
+static void
+unixraw_reset_timer(midi_writer_t *self)
+{
+}
+
+static void
+unixraw_close(midi_writer_t *self)
+{
+ close(fd);
+}
+
+midi_writer_t sfx_device_midi_unixraw = {
+ "unixraw-midi",
+ &unixraw_init,
+ &unixraw_set_option,
+ &unixraw_write,
+ &unixraw_delay,
+ NULL,
+ &unixraw_reset_timer,
+ &unixraw_close
+};
diff --git a/engines/sci/sfx/doc/README b/engines/sci/sfx/doc/README
new file mode 100644
index 0000000000..502ad7d619
--- /dev/null
+++ b/engines/sci/sfx/doc/README
@@ -0,0 +1,7 @@
+The files in this direcory are a bit outdated. There are some small non
+critical errors in "patch.001", they are obvious if you bother to read
+the source (which is correct). "sound01.txt" is just meant to give you a
+basic understanding of the structures invovled in
+SCI01/SCI1/SCI1.1/SCI32 sound resources.
+
+Ravi has made some really good documentation for SCI0.
diff --git a/engines/sci/sfx/doc/patch001.txt b/engines/sci/sfx/doc/patch001.txt
new file mode 100644
index 0000000000..0037eea9b4
--- /dev/null
+++ b/engines/sci/sfx/doc/patch001.txt
@@ -0,0 +1,118 @@
+SCI patch.001 Resource Format Revision 0.1
+Rickard Lind 1999-12-16
+
+The patch.001 file for Roland MT-32, MT-100, LAPC-1, CM-32L and CM-64.
+
+This specification will sometimes look very incomprehensible without some
+knowledge concerning the MT-32 MIDI implementation.
+Have a look at http://members.xoom.com/_XMCM/TomLewandowski/docs.html
+
+
+1. The header (494 bytes) which is always present.
+
+Offset Size Type Description
+----------------------------------------------------------------------------
+ 0x000 2 89 00 First Two Bytes
+ 0x002 20 ASCII String for MT-32 Display ("*It's Only A Model* ")
+ 0x016 20 ASCII String for MT-32 Display (" CAMELOT, CAMELOT! ")
+ 0x02a 20 ASCII String for MT-32 Display ("Ham & Jam & SpamAlot")
+ 0x03e 2 word MT-32 Master Volume
+ 0x040 1 Index in Predefined reverb settings at 0x04c (0-10)
+ 0x041 11 MT-32 SysEx block setting reverb
+ (last 3 bytes are dummies)
+ 0x04c 3 Predefined reverb setting #1 (Mode, Time, Level)
+ : : :
+ 0x06a 3 Predefined reverb setting #11
+ 0x06d 8 MT-32 Patch Memory #1 (see Patch Memory description below)
+ : : :
+ 0x1e5 8 MT-32 Patch Memory #48
+ 0x1ed 1 n = Number of Timbre Memory (0-64 userdefined instruments)
+----------------------------------------------------------------------------
+
+
+ Patch Memory description
+
+ Offset Description
+ --------------------------------------------------------------------
+ 0x00 Timbre Group (0 = Bank A, 1 = Bank B, 2 = Memory, 3 = Rythm)
+ 0x01 Timbre Number (0 - 63)
+ 0x02 Key Shift (0-48) [-24 - +24]
+ 0x03 Fine Tune (0-100) [-50 - +50]
+ 0x04 Bender Range (0-24)
+ 0x05 Assign Mode (0 = Poly 1, 1 = Poly 2, 2 = Poly 3, 3 = Poly 4)
+ 0x06 Reverb Switch (0 = OFF, 1 = ON)
+ 0x07 Dummy
+ --------------------------------------------------------------------
+
+ Mapping MT-32 to GM instruments is done with Timbre Group and
+ Timbre Number.
+
+ Instrument 0-63: Bank A, 0-63
+ Instrument 64-127: Bank B, 0-63
+
+
+2. The Timbre Memory block (if n > 0), offset relative to 0x1ee
+
+Offset Size Type Description
+------------------------------------------------------------------------------
+ 0x000 246 MT-32 Timbre Memory #1 (see Timbre Memory description below)
+ : : :
+ 0x??? 246 MT-32 Timbre Memory #n
+------------------------------------------------------------------------------
+
+
+ Timbre Memory description
+
+ Offset Size Description
+ -----------------------------------------------------------------------
+ 0x00 10 Timbre Name (ASCII String)
+ 0x0a See http://members.xoom.com/_XMCM/TomLewandowski/lapc1.txt
+ -----------------------------------------------------------------------
+
+
+3. Second MT-32 Patch Memory Block, offset realtive to 0x1ee + n * 246
+
+Offset Size Description
+---------------------------------------------------
+ 0x000 2 0xab 0xcd (if this this is not present
+ there is no second block)
+ 0x002 8 MT-32 Patch Memory #49
+ : : :
+ 0x17a 8 MT-32 Patch Memory #96
+---------------------------------------------------
+
+
+4. Block for setting up Patch Temporary Area (rythm part) and
+ System Area - Partial Reserve, offset relative to 0x370 + n * 246
+
+Offset Size Description
+---------------------------------------------------
+ 0x000 2 0xdc 0xba (if this this is not present
+ this block is non existent)
+ 0x002 4 Rythm Setup for Key #24 (see below)
+ : : :
+ 0x0fe 4 Rythm Setup for Key #87
+ 0x102 9 System Area - Partial Reserve
+---------------------------------------------------
+
+ Rythm Setup description
+ See http://members.xoom.com/_XMCM/TomLewandowski/lapc1.txt
+
+
+TODO:
+
+ * Clearly describe which parts are interesting for a quick and dirty
+ GeneralMidi/patch.001/FreeSCI implementation
+
+ * Describe how the Sierra MT-32 driver uses patch.001
+
+ * Make this readable to someone who has not been reading reference
+ manuals since early childhood
+
+ * SGML
+
+
+Revision history
+
+ Revision 0.1 - 1999-12-16
+ - First pre-release of the specification
diff --git a/engines/sci/sfx/doc/sound01.txt b/engines/sci/sfx/doc/sound01.txt
new file mode 100644
index 0000000000..8aae367da7
--- /dev/null
+++ b/engines/sci/sfx/doc/sound01.txt
@@ -0,0 +1,213 @@
+The SCI01+ sound resource format
+
+Originally written by Rickard Lind, 2000-01-05
+Extensively rewritten by Lars Skovlund, 2002-10-27
+Again updated by Lars Skovlund, 2005-10-12
+
+Used in:
+Quest for Glory II: Trial by Fire (QfG2)
+Christmas greeting card 1990 (CC1990)
+
+The magic number (84 00) is left out, offset 0 is directly after these two
+bytes.
+
+If you examine a SCI01 sound resource use "sciunpack --without-header" to
+get the pointers within the file correct for your hex viewer.
+
+DESCRIPTION
+-----------
+
+The SCI01 sound resource consists of a number of track lists and the
+tracks themselves. There is one track list for (almost) every piece of
+sound hardware supported by the game. Each track either contains track
+data for one specific channel or a digital sample.
+
+SCI1 resources are the same, except that sample chunks are no longer
+allowed (since they are now separate resources).
+
+ Optional Priority Header
+ ------------------------
+
+ Some SCI1 songs contain an 8-byte header before the track list. At
+ least on PC platforms, its data is mostly unused. The priority value
+ is used if the script does not override it.
+
+ offset size description
+ -------------------------------------------------------
+ 0 byte 0xf0 Priority header marker byte
+ 1 byte Recommended priority for this song
+ 2 6 bytes Apparently unused
+
+ Track List
+ ----------
+
+ The track list tells us which tracks are to be played on particular
+ hardware platforms. Each entry either terminates the previous list
+ or contains an entry for the current one.
+
+ List Termination
+ ----------------
+
+ offset size description
+ -----------------------
+ 0 byte 0xff
+ 1 byte Hardware ID of next list, 0xff if none
+
+ List Entry
+ ----------
+
+ offset size description
+ -----------------------
+ 0 byte 0
+ 1 byte 0
+ 2 word Data Chunk pointer
+ 4 word Data Chunk size
+
+ The very first list in a file looks a little odd, in that it
+ starts with a single byte which tells us the hardware ID
+ associated with the first list (0 in all the cases I've seen)
+ followed by list entries as usual.
+
+ Known Hardware IDs
+ ------------------
+
+ Some of these are used by multiple drivers, probably because they
+ support the same number of polyphonic voices. Note that the
+ hardware ID does not necessarily tell us whether samples are
+ supported - thus, the list for Roland MT-32 also contains sample
+ tracks, because the user may also have a Sound Blaster card
+ connected. SCI1 most likely has more hardware IDs than these.
+
+ 0x00 - Sound Blaster, Adlib
+ 0x06 - MT-32 with Sound Blaster (for digital audio)
+ 0x09 - CMS/Game Blaster
+ 0x0c - Roland MT-32
+ 0x12 - PC Speaker
+ 0x13 - IBM PS/1, Tandy 3-voice
+
+ Data Chunks
+ -----------
+
+ In the sound resources of QfG2 and CC1990 I've seen two types of Data
+ Chunks, Sample and MIDI channel track.
+
+
+ Sample Chunk
+ ------------
+
+ offset size description
+ -----------------------
+ 0 byte =0xfe
+ 1 byte !=0xfe (always 0 in QfG2 and CC1990)
+ 2 word Sample rate (Hz)
+ 4 word Sample length
+ 6 word Sample point 1 (begin?)
+ 8 word Sample point 2 (end?)
+ 10 Unsigned 8-bit mono sample data
+
+
+ MIDI channel track Chunk
+ ------------------------
+
+ This chunk begins with a 2 byte header. The low nibble of the
+ first byte indicates the channel number. The high nibble controls
+ certain aspects of (dynamic) track channel/hardware channel mapping.
+
+ The second byte tells us how many notes will be
+ playing simultaneously in the channel. From the third byte onward
+ is the MIDI track data which looks just like normal SCI0 MIDI
+ track data, but all status bytes are targeted at one specific MIDI
+ channel.
+
+Example, sound.833 from QfG2 (--without-header)
+-----------------------------------------------
+
+offset data description
+------------------------
+ 0000 00 Hardware ID for first track list
+ 0001 00 Track list continuation
+ 0002 00 Same hardware device
+ 0003 003F Data Chunk pointer (Little Endian)
+ 0005 0013 Data Chunk length (LE)
+ 0007 00 Track list continuation
+ 0008 00 Same hardware device
+ 0009 006A Data Chunk pointer (LE)
+ 000B 0015 Data Chunk length (LE)
+ 000D FF Next track list
+ 000E 09 for hardware device 0x09
+ 000F 00 Track list continuation
+ 0010 00 Same hardware device
+ 0011 003F Data Chunk pointer (LE)
+ 0013 0013 Data Chunk length (LE)
+ 0015 00 Track list continuation
+ 0016 00 Same hardware device
+ 0017 0052 Data Chunk pointer (LE)
+ 0019 0018 Data Chunk length (LE)
+ 001B 00 Track list continuation
+ 001C 00 Same hardware device
+ 001D 0094 Data Chunk pointer (LE)
+ 001F 0012 Data Chunk length (LE)
+ 0021 FF Next track list
+ 0022 0C for hardware device 0x0C
+ 0023 00 Track list continuation
+ 0024 00 Same hardware device
+ 0025 003F Data Chunk pointer (LE)
+ 0027 0013 Data Chunk length (LE)
+ 0029 00 Track list continuation
+ 002A 00 Same hardware device
+ 002B 0052 Data Chunk pointer (LE)
+ 002D 0018 Data Chunk length (LE)
+ 002F FF Next track list
+ 0030 13 for hardware device 0x13
+ 0031 00 Track list continuation
+ 0032 00 Same hardware device
+ 0033 003F Data Chunk pointer (LE)
+ 0035 0013 Data Chunk length (LE)
+ 0037 00 Track list continuation
+ 0038 00 Same hardware device
+ 0039 007F Data Chunk pointer (LE)
+ 003B 0015 Data Chunk length (LE)
+
+ 003D FF FF Sequence Control - End of Sequence Blocks
+ ------------------------------------------------------------
+ 003F 0F MIDI Track channel 15 (control channel)
+ 0040 01 One note playing on track (probably just to satisfy the
+ MIDI engine)
+ 0041 MIDI Track data like SCI0
+ 0052 02 MIDI Track channel 2
+ 0053 02 Two notes playing on track
+ 0054 MIDI Track data like SCI0
+ 006A 03 MIDI Track channel 3
+ 006B 01 One note playing on track
+ 006C MIDI Track data like SCI0
+ 007F 0A MIDI Track channel 10
+ 0080 01 One note playing on track
+ 0081 MIDI Track data like SCI0
+ 0094 02 MIDI Track channel 2
+ 0095 01 One note playing on track
+ 0096 MIDI Track data like SCI0
+
+Addendum (provided by Lars Skovlund)
+------------------------------------
+
+First of all, tracks do not loop individually. No loop signals are
+reported.
+
+Absolute cues are generally stored in the signal selector, and
+cumulative cues are generally stored in the dataInc selector, with
+some interesting twists:
+
+1. The server's record of the absolute cue value is reset as part of
+ UPDATE_CUES.
+2. When a cumulative cue is reported to the VM object, it will be
+ placed in _both_ fields. In such a case, a constant of 0x7f will be
+ added to the _signal_ selector only, to be able to distinguish the
+ two kinds of cue (this has already been coded).
+3. The above only happens if the sound does not use absolute cues
+ (i.e. if the signal is 0 a priori). Note that, because of 1)
+ above, this does not cause problems neither with successive
+ cumulative cues nor with mixed cumulative/absolute cues.
+4. A signal of 0xff will stop the sound object playing. This may be
+ for purely internal purposes.
+5. There no longer is a field indicating the amount of increment for
+ a cue.
diff --git a/engines/sci/sfx/iterator.c b/engines/sci/sfx/iterator.c
new file mode 100644
index 0000000000..f6c123a082
--- /dev/null
+++ b/engines/sci/sfx/iterator.c
@@ -0,0 +1,2113 @@
+/***************************************************************************
+ iterator.c Copyright (C) 2001..04 Christoph Reichenbach
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+/* Song iterators */
+
+#include <stdio.h>
+#include <sfx_iterator_internal.h>
+#include <sfx_player.h>
+#include <resource.h>
+#include <sci_memory.h>
+
+static const int MIDI_cmdlen[16] = {0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 2, 2, 2, 1, 1, 2, 0};
+
+/*#define DEBUG_DECODING*/
+/*#define DEBUG_VERBOSE*/
+
+void
+print_tabs_id(int nr, songit_id_t id)
+{
+ while (nr-- > 0)
+ fprintf(stderr, "\t");
+
+ fprintf(stderr, "[%08lx] ", id);
+}
+
+#ifndef HAVE_MEMCHR
+static void *
+memchr(void *_data, int c, int n)
+{
+ unsigned char *data = (unsigned char *) _data;
+
+ while (n && !(*data == c)) {
+ ++data;
+ --n;
+ }
+
+ if (n)
+ return data;
+ else
+ return NULL;
+}
+#endif
+
+static void
+_common_init(base_song_iterator_t *self)
+{
+ self->fade.action = FADE_ACTION_NONE;
+ self->resetflag = 0;
+ self->loops = 0;
+ self->priority = 0;
+}
+
+
+/************************************/
+/*-- SCI0 iterator implementation --*/
+/************************************/
+
+#define SCI0_MIDI_OFFSET 33
+#define SCI0_END_OF_SONG 0xfc /* proprietary MIDI command */
+
+#define SCI0_PCM_SAMPLE_RATE_OFFSET 0x0e
+#define SCI0_PCM_SIZE_OFFSET 0x20
+#define SCI0_PCM_DATA_OFFSET 0x2c
+
+#define CHECK_FOR_END_ABSOLUTE(offset) \
+ if (offset > self->size) { \
+ fprintf(stderr, SIPFX "Reached end of song without terminator (%x/%x) at %d!\n", offset, self->size, __LINE__); \
+ return SI_FINISHED; \
+ }
+
+#define CHECK_FOR_END(offset_augment) \
+ if ((channel->offset + (offset_augment)) > channel->end) { \
+ channel->state = SI_STATE_FINISHED; \
+ fprintf(stderr, SIPFX "Reached end of track %d without terminator (%x+%x/%x) at %d!\n", channel->id, channel->offset, offset_augment, channel->end, __LINE__); \
+ return SI_FINISHED; \
+ }
+
+
+static inline int
+_parse_ticks(byte *data, int *offset_p, int size)
+{
+ int ticks = 0;
+ int tempticks;
+ int offset = 0;
+
+ do {
+ tempticks = data[offset++];
+ ticks += (tempticks == SCI_MIDI_TIME_EXPANSION_PREFIX)?
+ SCI_MIDI_TIME_EXPANSION_LENGTH : tempticks;
+ } while (tempticks == SCI_MIDI_TIME_EXPANSION_PREFIX
+ && offset < size);
+
+ if (offset_p)
+ *offset_p = offset;
+
+ return ticks;
+}
+
+
+static int
+_sci0_read_next_command(sci0_song_iterator_t *self,
+ unsigned char *buf, int *result);
+
+
+static int
+_sci0_get_pcm_data(sci0_song_iterator_t *self,
+ sfx_pcm_config_t *format,
+ int *xoffset,
+ unsigned int *xsize);
+
+#define PARSE_FLAG_LOOPS_UNLIMITED (1 << 0) /* Unlimited # of loops? */
+#define PARSE_FLAG_PARAMETRIC_CUE (1 << 1) /* Assume that cues take an additional "cue value" argument */
+/* This implements a difference between SCI0 and SCI1 cues. */
+
+void
+_reset_synth_channels(base_song_iterator_t *self, song_iterator_channel_t *channel)
+{
+ int i;
+ byte buf[5];
+ tell_synth_func *tell = sfx_get_player_tell_func();
+
+ for (i = 0; i < MIDI_CHANNELS; i++)
+ {
+ if (channel->saw_notes & (1 << i))
+ {
+ buf[0] = 0xe0 | i; /* Pitch bend */
+ buf[1] = 0x80; /* Wheel center */
+ buf[2] = 0x40;
+ if (tell)
+ tell(3, buf);
+ /* TODO: Reset other controls? */
+ }
+ }
+}
+
+static int
+_parse_sci_midi_command(base_song_iterator_t *self, unsigned char *buf, int *result,
+ song_iterator_channel_t *channel,
+ int flags)
+{
+ unsigned char cmd;
+ int paramsleft;
+ int midi_op;
+ int midi_channel;
+
+ channel->state = SI_STATE_DELTA_TIME;
+
+ cmd = self->data[channel->offset++];
+
+ if (!(cmd & 0x80)) {
+ /* 'Running status' mode */
+ channel->offset--;
+ cmd = channel->last_cmd;
+ }
+
+ if (cmd == 0xfe)
+ {
+ fprintf(stderr, "song iterator subsystem: Corrupted sound resource detected.\n");
+ return SI_FINISHED;
+ }
+
+ midi_op = cmd >> 4;
+ midi_channel = cmd & 0xf;
+ paramsleft = MIDI_cmdlen[midi_op];
+ channel->saw_notes |= 1 << midi_channel;
+
+#if 0
+if (1) {
+ fprintf(stderr, "[IT]: off=%x, cmd=%02x, takes %d args ",
+ channel->offset - 1, cmd, paramsleft);
+ fprintf(stderr, "[%02x %02x <%02x> %02x %02x %02x]\n",
+ self->data[channel->offset-3],
+ self->data[channel->offset-2],
+ self->data[channel->offset-1],
+ self->data[channel->offset],
+ self->data[channel->offset+1],
+ self->data[channel->offset+2]);
+}
+#endif
+
+ buf[0] = cmd;
+
+
+ CHECK_FOR_END(paramsleft);
+ memcpy(buf + 1, self->data + channel->offset, paramsleft);
+ *result = 1 + paramsleft;
+
+ channel->offset += paramsleft;
+
+ channel->last_cmd = cmd;
+
+ /* Are we supposed to play this channel? */
+ if (
+ /* First, exclude "global" properties-- such as cues-- from consideration */
+ (midi_op < 0xf
+ && !(cmd == SCI_MIDI_SET_SIGNAL)
+ && !(SCI_MIDI_CONTROLLER(cmd)
+ && buf[1] == SCI_MIDI_CUMULATIVE_CUE))
+
+ /* Next, check if the channel is allowed */
+ && (!((1 << midi_channel) & channel->playmask)))
+ return /* Execute next command */
+ self->next((song_iterator_t *) self, buf, result);
+
+
+ if (cmd == SCI_MIDI_EOT) {
+ /* End of track? */
+ _reset_synth_channels(self, channel);
+/* fprintf(stderr, "eot; loops = %d, notesplayed=%d\n", self->loops, channel->notes_played);*/
+ if (self->loops > 1 /* && channel->notes_played*/) {
+ /* If allowed, decrement the number of loops */
+ if (!(flags & PARSE_FLAG_LOOPS_UNLIMITED))
+ *result = --self->loops;
+
+#ifdef DEBUG_DECODING
+ fprintf(stderr, "%s L%d: (%p):%d Looping ", __FILE__, __LINE__, self, channel->id);
+ if (flags & PARSE_FLAG_LOOPS_UNLIMITED)
+ fprintf(stderr, "(indef.)");
+ else
+ fprintf(stderr, "(%d)", self->loops);
+ fprintf(stderr, " %x -> %x\n",
+ channel->offset, channel->loop_offset);
+#endif
+ channel->offset = channel->loop_offset;
+ channel->notes_played = 0;
+ channel->state = SI_STATE_DELTA_TIME;
+ channel->total_timepos = channel->loop_timepos;
+ channel->last_cmd = 0xfe;
+ fprintf(stderr, "Looping song iterator %08lx.\n", self->ID);
+ return SI_LOOP;
+ } else {
+ channel->state = SI_STATE_FINISHED;
+#ifdef DEBUG_DECODING
+ fprintf(stderr, "%s L%d: (%p):%d EOT because"
+ " %d notes, %d loops\n",
+ __FILE__, __LINE__, self, channel->id,
+ channel->notes_played, self->loops);
+#endif
+ return SI_FINISHED;
+ }
+
+ } else if (cmd == SCI_MIDI_SET_SIGNAL) {
+ if (buf[1] == SCI_MIDI_SET_SIGNAL_LOOP) {
+ channel->loop_offset = channel->offset;
+ channel->loop_timepos = channel->total_timepos;
+
+ return /* Execute next command */
+ self->next((song_iterator_t *) self, buf, result);
+ } else {
+ /* Used to be conditional <= 127 */
+ *result = buf[1]; /* Absolute cue */
+ return SI_ABSOLUTE_CUE;
+ }
+ } else if (SCI_MIDI_CONTROLLER(cmd)) {
+ switch (buf[1]) {
+
+ case SCI_MIDI_CUMULATIVE_CUE:
+ if (flags & PARSE_FLAG_PARAMETRIC_CUE)
+ self->ccc += buf[2];
+ else { /* No parameter to CC */
+ self->ccc++;
+/* channel->offset--; */
+ }
+ *result = self->ccc;
+ return SI_RELATIVE_CUE;
+
+ case SCI_MIDI_RESET_ON_SUSPEND:
+ self->resetflag = buf[2];
+ break;
+
+ case SCI_MIDI_SET_POLYPHONY:
+ self->polyphony[midi_channel] = buf[2];
+
+#if 0
+ {
+ int i;
+ int voices = 0;
+ for (i = 0; i < ((sci1_song_iterator_t *) self)->channels_nr; i++)
+ {
+ voices += self->polyphony[i];
+ }
+
+ sciprintf("SET_POLYPHONY(%d, %d) for a total of %d voices\n", midi_channel, buf[2], voices);
+ sciprintf("[iterator-1] DEBUG: Polyphony = [ ");
+ for (i = 0; i < ((sci1_song_iterator_t *) self)->channels_nr; i++)
+ sciprintf("%d ", self->polyphony[i]);
+ sciprintf("]\n");
+ sciprintf("[iterator-1] DEBUG: Importance = [ ");
+ for (i = 0; i < ((sci1_song_iterator_t *) self)->channels_nr; i++)
+ sciprintf("%d ", self->importance[i]);
+ sciprintf("]\n");
+ }
+#endif
+ break;
+
+ case SCI_MIDI_SET_REVERB:
+ break;
+
+ case SCI_MIDI_CHANNEL_MUTE:
+ sciprintf("CHANNEL_MUTE(%d, %d)\n", midi_channel, buf[2]);
+ break;
+
+ case SCI_MIDI_HOLD:
+ {
+ // Safe cast: This controller is only used in SCI1
+ sci1_song_iterator_t *self1 = (sci1_song_iterator_t *) self;
+
+ if (buf[2] == self1->hold)
+ {
+ channel->offset = channel->initial_offset;
+ channel->notes_played = 0;
+ channel->state = SI_STATE_COMMAND;
+ channel->total_timepos = 0;
+
+ self1->channels_looped = self1->active_channels-1;
+
+ return SI_LOOP;
+ }
+
+ break;
+ }
+ case 0x04: /* UNKNOWN NYI (happens in LSL2 gameshow) */
+ case 0x46: /* UNKNOWN NYI (happens in LSL3 binoculars) */
+ case 0x61: /* UNKNOWN NYI (special for adlib? Iceman) */
+ case 0x73: /* UNKNOWN NYI (happens in Hoyle) */
+ case 0xd1: /* UNKNOWN NYI (happens in KQ4 when riding the unicorn) */
+ return /* Execute next command */
+ self->next((song_iterator_t *) self, buf, result);
+
+ case 0x01: /* modulation */
+ case 0x07: /* volume */
+ case 0x0a: /* panpot */
+ case 0x0b: /* expression */
+ case 0x40: /* hold */
+ case 0x79: /* reset all */
+ /* No special treatment neccessary */
+ break;
+
+ }
+ return 0;
+
+ } else {
+ if ((cmd & 0xf0) == 0x90) /* note on? */
+ channel->notes_played++;
+
+ /* Process as normal MIDI operation */
+ return 0;
+ }
+}
+
+
+static int
+_sci_midi_process_state(base_song_iterator_t *self, unsigned char *buf, int *result,
+ song_iterator_channel_t *channel,
+ int flags)
+{
+ CHECK_FOR_END(0);
+
+ switch (channel->state) {
+
+ case SI_STATE_PCM: {
+ if (*(self->data + channel->offset) == 0
+ && *(self->data + channel->offset + 1) == SCI_MIDI_EOT)
+ /* Fake one extra tick to trick the interpreter into not killing the song iterator right away */
+ channel->state = SI_STATE_PCM_MAGIC_DELTA;
+ else
+ channel->state = SI_STATE_DELTA_TIME;
+ return SI_PCM;
+ }
+
+ case SI_STATE_PCM_MAGIC_DELTA: {
+ sfx_pcm_config_t format;
+ int offset;
+ unsigned int size;
+ int delay;
+ if (_sci0_get_pcm_data((sci0_song_iterator_t *) self, &format, &offset, &size))
+ return SI_FINISHED; /* 'tis broken */
+ channel->state = SI_STATE_FINISHED;
+ delay = (size * 50 + format.rate - 1) / format.rate; /* number of ticks to completion*/
+
+ fprintf(stderr, "delaying %d ticks\n", delay);
+ return delay;
+ }
+
+ case SI_STATE_UNINITIALISED:
+ fprintf(stderr, SIPFX "Attempt to read command from uninitialized iterator!\n");
+ self->init((song_iterator_t *) self);
+ return self->next((song_iterator_t *) self, buf, result);
+
+ case SI_STATE_FINISHED:
+ return SI_FINISHED;
+
+ case SI_STATE_DELTA_TIME: {
+ int offset;
+ int ticks = _parse_ticks(self->data + channel->offset,
+ &offset,
+ self->size - channel->offset);
+
+ channel->offset += offset;
+ channel->delay += ticks;
+ channel->timepos_increment = ticks;
+
+ CHECK_FOR_END(0);
+
+ channel->state = SI_STATE_COMMAND;
+
+ if (ticks)
+ return ticks;
+ }
+
+ /* continute otherwise... */
+
+ case SI_STATE_COMMAND: {
+ int retval;
+ channel->total_timepos += channel->timepos_increment;
+ channel->timepos_increment = 0;
+
+ retval = _parse_sci_midi_command(self, buf, result,
+ channel, flags);
+
+ if (retval == SI_FINISHED) {
+ if (self->active_channels)
+ --(self->active_channels);
+#ifdef DEBUG_DECODING
+ fprintf(stderr, "%s L%d: (%p):%d Finished channel, %d channels left\n",
+ __FILE__, __LINE__, self, channel->id,
+ self->active_channels);
+#endif
+ /* If we still have channels left... */
+ if (self->active_channels) {
+ return self->next((song_iterator_t *) self, buf, result);
+ }
+
+ /* Otherwise, we have reached the end */
+ self->loops = 0;
+ }
+
+ return retval;
+ }
+
+ default:
+ fprintf(stderr, SIPFX "Invalid iterator state %d!\n",
+ channel->state);
+ BREAKPOINT();
+ return SI_FINISHED;
+ }
+}
+
+static inline int
+_sci_midi_process(base_song_iterator_t *self, unsigned char *buf, int *result,
+ song_iterator_channel_t *channel,
+ int flags)
+{
+ return _sci_midi_process_state(self, buf, result,
+ channel,
+ flags);
+}
+
+static int
+_sci0_read_next_command(sci0_song_iterator_t *self, unsigned char *buf,
+ int *result)
+{
+ return _sci_midi_process((base_song_iterator_t *) self, buf, result,
+ &(self->channel),
+ PARSE_FLAG_PARAMETRIC_CUE);
+}
+
+
+static inline int
+_sci0_header_magic_p(unsigned char *data, int offset, int size)
+{
+ if (offset + 0x10 > size)
+ return 0;
+ return
+ (data[offset] == 0x1a)
+ && (data[offset + 1] == 0x00)
+ && (data[offset + 2] == 0x01)
+ && (data[offset + 3] == 0x00);
+}
+
+
+static int
+_sci0_get_pcm_data(sci0_song_iterator_t *self,
+ sfx_pcm_config_t *format,
+ int *xoffset,
+ unsigned int *xsize)
+{
+ int tries = 2;
+ int found_it = 0;
+ unsigned char *pcm_data;
+ int size;
+ unsigned int offset = SCI0_MIDI_OFFSET;
+
+ if (self->data[0] != 2)
+ return 1;
+ /* No such luck */
+
+ while ((tries--) && (offset < self->size) && (!found_it)) {
+ /* Search through the garbage manually */
+ unsigned char *fc = (unsigned char*)memchr(self->data + offset,
+ SCI0_END_OF_SONG,
+ self->size - offset);
+
+ if (!fc) {
+ fprintf(stderr, SIPFX "Warning: Playing unterminated"
+ " song!\n");
+ return 1;
+ }
+
+ /* add one to move it past the END_OF_SONG marker */
+ offset = fc - self->data + 1;
+
+
+ if (_sci0_header_magic_p(self->data, offset, self->size))
+ found_it = 1;
+ }
+
+ if (!found_it) {
+ fprintf(stderr, SIPFX
+ "Warning: Song indicates presence of PCM, but"
+ " none found (finally at offset %04x)\n", offset);
+
+ return 1;
+ }
+
+ pcm_data = self->data + offset;
+
+ size = getUInt16(pcm_data + SCI0_PCM_SIZE_OFFSET);
+
+ /* Two of the format parameters are fixed by design: */
+ format->format = SFX_PCM_FORMAT_U8;
+ format->stereo = SFX_PCM_MONO;
+ format->rate = getUInt16(pcm_data + SCI0_PCM_SAMPLE_RATE_OFFSET);
+
+ if (offset + SCI0_PCM_DATA_OFFSET + size != self->size) {
+ int d = offset + SCI0_PCM_DATA_OFFSET + size - self->size;
+
+ fprintf(stderr, SIPFX
+ "Warning: PCM advertizes %d bytes of data, but %d"
+ " bytes are trailing in the resource!\n",
+ size, self->size - (offset + SCI0_PCM_DATA_OFFSET));
+
+ if (d > 0)
+ size -= d; /* Fix this */
+ }
+
+ *xoffset = offset;
+ *xsize = size;
+
+ return 0;
+ }
+
+static sfx_pcm_feed_t *
+_sci0_check_pcm(sci0_song_iterator_t *self)
+{
+ sfx_pcm_config_t format;
+ int offset;
+ unsigned int size;
+ if (_sci0_get_pcm_data(self, &format, &offset, &size))
+ return NULL;
+
+ self->channel.state
+ = SI_STATE_FINISHED; /* Don't play both PCM and music */
+
+ return sfx_iterator_make_feed(self->data,
+ offset + SCI0_PCM_DATA_OFFSET,
+ size,
+ format);
+}
+
+static song_iterator_t *
+_sci0_handle_message(sci0_song_iterator_t *self, song_iterator_message_t msg)
+{
+ if (msg.recipient == _SIMSG_BASE) {
+ switch (msg.type) {
+
+ case _SIMSG_BASEMSG_PRINT:
+ print_tabs_id(msg.args[0].i, self->ID);
+ fprintf(stderr, "SCI0: dev=%d, active-chan=%d, size=%d, loops=%d\n",
+ self->device_id, self->active_channels, self->size,
+ self->loops);
+ break;
+
+ case _SIMSG_BASEMSG_SET_LOOPS:
+ self->loops = msg.args[0].i;
+ break;
+
+ case _SIMSG_BASEMSG_CLONE: {
+ int tsize = sizeof(sci0_song_iterator_t);
+ base_song_iterator_t *mem = (base_song_iterator_t*)sci_malloc(tsize);
+ memcpy(mem, self, tsize);
+ sci_refcount_incref(mem->data);
+#ifdef DEBUG_VERBOSE
+fprintf(stderr, "** CLONE INCREF for new %p from %p at %p\n", mem, self, mem->data);
+#endif
+ return (struct _song_iterator *) mem; /* Assume caller has another copy of this */
+ }
+
+ case _SIMSG_BASEMSG_STOP: {
+ songit_id_t sought_id = msg.ID;
+
+ if (sought_id == self->ID)
+ self->channel.state = SI_STATE_FINISHED;
+ break;
+ }
+
+ case _SIMSG_BASEMSG_SET_PLAYMASK: {
+ int i;
+ self->device_id = msg.args[0].i;
+
+ /* Set all but the rhytm channel mask bits */
+ self->channel.playmask &= ~(1 << MIDI_RHYTHM_CHANNEL);
+
+ for (i = 0; i < MIDI_CHANNELS; i++)
+ if (self->data[2 + (i << 1)] & self->device_id
+ && i != MIDI_RHYTHM_CHANNEL)
+ self->channel.playmask |= (1 << i);
+ }
+ break;
+
+ case _SIMSG_BASEMSG_SET_RHYTHM:
+ self->channel.playmask &= ~(1 << MIDI_RHYTHM_CHANNEL);
+ if (msg.args[0].i)
+ self->channel.playmask |= (1 << MIDI_RHYTHM_CHANNEL);
+ break;
+
+ case _SIMSG_BASEMSG_SET_FADE:
+ {
+ fade_params_t *fp = (fade_params_t *) msg.args[0].p;
+ self->fade.action = fp->action;
+ self->fade.final_volume = fp->final_volume;
+ self->fade.ticks_per_step = fp->ticks_per_step;
+ self->fade.step_size = fp->step_size;
+ break;
+ }
+
+ default:
+ return NULL;
+ }
+
+ return (song_iterator_t *)self;
+ }
+ return NULL;
+}
+
+static int
+_sci0_get_timepos(sci0_song_iterator_t *self)
+{
+ return self->channel.total_timepos;
+}
+
+static void
+_base_init_channel(song_iterator_channel_t *channel, int id, int offset,
+ int end)
+{
+ channel->playmask = PLAYMASK_NONE; /* Disable all channels */
+ channel->id = id;
+ channel->notes_played = 0;
+ channel->state = SI_STATE_DELTA_TIME;
+ channel->loop_timepos = 0;
+ channel->total_timepos = 0;
+ channel->timepos_increment = 0;
+ channel->delay = 0; /* Only used for more than one channel */
+ channel->last_cmd = 0xfe;
+
+ channel->offset
+ = channel->loop_offset
+ = channel->initial_offset
+ = offset;
+ channel->end = end;
+ channel->saw_notes = 0;
+}
+
+static void
+_sci0_init(sci0_song_iterator_t *self)
+{
+ _common_init((base_song_iterator_t *) self);
+
+ self->ccc = 0; /* Reset cumulative cue counter */
+ self->active_channels = 1;
+ _base_init_channel(&(self->channel), 0, SCI0_MIDI_OFFSET, self->size);
+ _reset_synth_channels((base_song_iterator_t *) self,
+ &(self->channel));
+ self->delay_remaining = 0;
+
+ if (self->data[0] == 2) /* Do we have an embedded PCM? */
+ self->channel.state = SI_STATE_PCM;
+}
+
+
+static void
+_sci0_cleanup(sci0_song_iterator_t *self)
+{
+#ifdef DEBUG_VERBOSE
+fprintf(stderr, "** FREEING it %p: data at %p\n", self, self->data);
+#endif
+ if (self->data)
+ sci_refcount_decref(self->data);
+ self->data = NULL;
+}
+
+/***************************/
+/*-- SCI1 song iterators --*/
+/***************************/
+
+#define SCI01_INVALID_DEVICE 0xff
+
+/* First index determines whether DSP output is supported */
+static int sci0_to_sci1_device_map[][2] = {
+ {0x06, 0x0c}, /* MT-32 */
+ {0xff, 0xff}, /* YM FB-01 */
+ {0x00, 0x00}, /* CMS/Game Blaster-- we assume OPL/2 here... */
+ {0xff, 0xff}, /* Casio MT540/CT460 */
+ {0x13, 0x13}, /* Tandy 3-voice */
+ {0x12, 0x12}, /* PC speaker */
+ {0xff, 0xff},
+ {0xff, 0xff},
+}; /* Maps bit number to device ID */
+
+#define SONGDATA(x) self->data[offset + (x)]
+#define SCI1_CHANDATA(off) self->data[channel->offset + (off)]
+
+static int
+_sci1_sample_init(sci1_song_iterator_t *self, int offset)
+{
+ sci1_sample_t *sample, **seekerp;
+ int rate;
+ int length;
+ int begin;
+ int end;
+
+ CHECK_FOR_END_ABSOLUTE(offset + 10);
+ if (self->data[offset + 1] != 0)
+ sciprintf("[iterator-1] In sample at offset 0x04x: Byte #1 is %02x instead of zero\n",
+ self->data[offset + 1]);
+
+ rate = getInt16(self->data + offset + 2);
+ length = getUInt16(self->data + offset + 4);
+ begin = getInt16(self->data + offset + 6);
+ end = getInt16(self->data + offset + 8);
+
+ CHECK_FOR_END_ABSOLUTE(offset + 10 + length);
+
+ sample = (sci1_sample_t*)sci_malloc(sizeof(sci1_sample_t));
+ sample->delta = begin;
+ sample->size = length;
+ sample->data = self->data + offset + 10;
+
+#ifdef DEBUG_VERBOSE
+ fprintf(stderr, "[SAMPLE] %x/%x/%x/%x l=%x\n",
+ offset + 10, begin, end, self->size, length);
+#endif
+
+ sample->format.format = SFX_PCM_FORMAT_U8;
+ sample->format.stereo = SFX_PCM_MONO;
+ sample->format.rate = rate;
+
+ sample->announced = 0;
+
+ /* Perform insertion sort */
+ seekerp = &(self->next_sample);
+
+ while (*seekerp && (*seekerp)->delta < begin)
+ seekerp = &((*seekerp)->next);
+
+ sample->next = *seekerp;
+ *seekerp = sample;
+
+ return 0; /* Everything's fine */
+}
+
+
+static int
+_sci1_song_init(sci1_song_iterator_t *self)
+{
+ sci1_sample_t *seeker;
+ int last_time;
+ int offset = 0;
+ self->channels_nr = 0;
+ self->next_sample = 0;
+// self->device_id = 0x0c;
+
+ CHECK_FOR_END_ABSOLUTE(0);
+ if (SONGDATA(0) == 0xf0)
+ {
+ self->priority = SONGDATA(1);
+
+ offset += 8;
+ }
+
+ while (SONGDATA(0) != 0xff
+ && SONGDATA(0) != self->device_id) {
+ offset++;
+ CHECK_FOR_END_ABSOLUTE(offset + 1);
+ while (SONGDATA(0) != 0xff) {
+ CHECK_FOR_END_ABSOLUTE(offset + 7);
+ offset += 6;
+ }
+ offset++;
+ }
+
+ if (SONGDATA(0) == 0xff) {
+ sciprintf("[iterator-1] Song does not support"
+ " hardware 0x%02x\n",
+ self->device_id);
+ return 1;
+ }
+
+ offset++;
+
+ while (SONGDATA(0) != 0xff) { /* End of list? */
+ int track_offset;
+ int end;
+ offset += 2;
+
+ CHECK_FOR_END_ABSOLUTE(offset + 4);
+
+ track_offset = getUInt16(self->data + offset);
+ end = getUInt16(self->data + offset + 2);
+
+ CHECK_FOR_END_ABSOLUTE(track_offset - 1);
+
+ if (self->data[track_offset] == 0xfe) {
+ if (_sci1_sample_init(self, track_offset))
+ return 1; /* Error */
+ } else {
+ /* Regular MIDI channel */
+ if (self->channels_nr >= MIDI_CHANNELS) {
+ sciprintf("[iterator-1] Warning: Song has more than %d channels, cutting them off\n",
+ MIDI_CHANNELS);
+ break; /* Scan for remaining samples */
+ } else {
+ int channel_nr
+ = self->data[track_offset] & 0xf;
+ song_iterator_channel_t *channel =
+ &(self->channels[self->channels_nr++]);
+
+ if (self->data[track_offset] & 0xf0)
+ printf("Channel %d has mapping bits %02x\n",
+ channel_nr, self->data[track_offset] & 0xf0);
+
+ _base_init_channel(channel,
+ channel_nr,
+ /* Skip over header bytes: */
+ track_offset + 2,
+ track_offset + end);
+ _reset_synth_channels((base_song_iterator_t *) self,
+ channel);
+
+ self->polyphony[self->channels_nr - 1]
+ = SCI1_CHANDATA(-1);
+ self->importance[self->channels_nr - 1]
+ = self->polyphony[self->channels_nr - 1] >> 4;
+ self->polyphony[self->channels_nr - 1] &= 15;
+
+ channel->playmask = ~0; /* Enable all */
+ self->channel_mask |= (1 << channel_nr);
+
+ CHECK_FOR_END_ABSOLUTE(offset + end);
+ }
+ }
+ offset += 4;
+ CHECK_FOR_END_ABSOLUTE(offset);
+ }
+
+ /* Now ensure that sapmle deltas are relative to the previous sample */
+ seeker = self->next_sample;
+ last_time = 0;
+ self->active_channels = self->channels_nr;
+ self->channels_looped = 0;
+
+ while (seeker) {
+ int prev_last_time = last_time;
+ sciprintf("[iterator-1] Detected sample: %d Hz, %d bytes at time %d\n",
+ seeker->format.rate, seeker->size, seeker->delta);
+ last_time = seeker->delta;
+ seeker->delta -= prev_last_time;
+ seeker = seeker->next;
+ }
+
+ return 0; /* Success */
+}
+
+#undef SONGDATA
+
+static inline int
+_sci1_get_smallest_delta(sci1_song_iterator_t *self)
+{
+ int i, d = -1;
+ for (i = 0; i < self->channels_nr; i++)
+ if (self->channels[i].state == SI_STATE_COMMAND
+ && (d == -1 || self->channels[i].delay < d))
+ d = self->channels[i].delay;
+
+ if (self->next_sample && self->next_sample->delta < d)
+ return self->next_sample->delta;
+ else
+ return d;
+}
+
+static inline void
+_sci1_update_delta(sci1_song_iterator_t *self, int delta)
+{
+ int i;
+
+ if (self->next_sample)
+ self->next_sample->delta -= delta;
+
+ for (i = 0; i < self->channels_nr; i++)
+ if (self->channels[i].state == SI_STATE_COMMAND)
+ self->channels[i].delay -= delta;
+}
+
+static inline int
+_sci1_no_delta_time(sci1_song_iterator_t *self)
+{ /* Checks that none of the channels is waiting for its delta to be read */
+ int i;
+
+ for (i = 0; i < self->channels_nr; i++)
+ if (self->channels[i].state == SI_STATE_DELTA_TIME)
+ return 0;
+
+ return 1;
+}
+
+static void
+_sci1_dump_state(sci1_song_iterator_t *self)
+{
+ int i;
+
+ sciprintf("-- [%p] ------------------------\n", self);
+ for (i = 0; i < self->channels_nr; i++) {
+ int j;
+ sciprintf("%d(s%02d): d-%d:\t(%x/%x) ",
+ self->channels[i].id,
+ self->channels[i].state,
+ self->channels[i].delay,
+ self->channels[i].offset,
+ self->channels[i].end);
+ for (j = -3; j < 9; j++) {
+ if (j == 0)
+ sciprintf(">");
+ else
+ sciprintf(" ");
+
+ sciprintf("%02x", self->data[self->channels[i].offset+j]);
+
+ if (j == 0)
+ sciprintf("<");
+ else
+ sciprintf(" ");
+ }
+ sciprintf("\n");
+ }
+ if (self->next_sample) {
+ sciprintf("\t[sample %d]\n",
+ self->next_sample->delta);
+ }
+ sciprintf("------------------------------------------\n");
+}
+
+#define COMMAND_INDEX_NONE -1
+#define COMMAND_INDEX_PCM -2
+
+static inline int /* Determine the channel # of the next active event, or -1 */
+_sci1_command_index(sci1_song_iterator_t *self)
+{
+ int i;
+ int base_delay = 0x7ffffff;
+ int best_chan = COMMAND_INDEX_NONE;
+
+ for (i = 0; i < self->channels_nr; i++)
+ if ((self->channels[i].state != SI_STATE_PENDING)
+ && (self->channels[i].state != SI_STATE_FINISHED)) {
+
+ if ((self->channels[i].state == SI_STATE_DELTA_TIME)
+ && (self->channels[i].delay == 0))
+ return i;
+ /* First, read all unknown delta times */
+
+ if (self->channels[i].delay < base_delay) {
+ best_chan = i;
+ base_delay = self->channels[i].delay;
+ }
+ }
+
+ if (self->next_sample && base_delay >= self->next_sample->delta)
+ return COMMAND_INDEX_PCM;
+
+ return best_chan;
+}
+
+
+static sfx_pcm_feed_t *
+_sci1_get_pcm(sci1_song_iterator_t *self)
+{
+ if (self->next_sample
+ && self->next_sample->delta <= 0) {
+ sci1_sample_t *sample = self->next_sample;
+ sfx_pcm_feed_t *feed
+ = sfx_iterator_make_feed(self->data,
+ sample->data - self->data,
+ sample->size,
+ sample->format);
+
+ self->next_sample = self->next_sample->next;
+
+ sci_free(sample);
+
+ return feed;
+ } else
+ return NULL;
+}
+
+
+static int
+_sci1_process_next_command(sci1_song_iterator_t *self,
+ unsigned char *buf, int *result)
+{
+ int retval = -42; /* Shouldn't happen, but gcc doesn't agree */
+ int chan;
+
+ if (!self->initialised) {
+ sciprintf("[iterator-1] DEBUG: Initialising for %d\n",
+ self->device_id);
+ self->initialised = 1;
+ if (_sci1_song_init(self))
+ return SI_FINISHED;
+ }
+
+
+ if (self->delay_remaining) {
+ int delay = self->delay_remaining;
+ self->delay_remaining = 0;
+ return delay;
+ }
+
+ do {
+ chan = _sci1_command_index(self);
+
+ if (chan == COMMAND_INDEX_NONE) {
+ return SI_FINISHED;
+ }
+
+ if (chan == COMMAND_INDEX_PCM) {
+
+ if (self->next_sample->announced) {
+ /* Already announced; let's discard it */
+ sfx_pcm_feed_t *feed
+ = _sci1_get_pcm(self);
+ feed->destroy(feed);
+ } else {
+ int delay = self->next_sample->delta;
+
+ if (delay) {
+ _sci1_update_delta(self, delay);
+ return delay;
+ }
+ /* otherwise we're touching a PCM */
+ self->next_sample->announced = 1;
+ return SI_PCM;
+ }
+ } else { /* Not a PCM */
+
+ retval = _sci_midi_process((base_song_iterator_t *) self,
+ buf, result,
+ &(self->channels[chan]),
+ PARSE_FLAG_LOOPS_UNLIMITED);
+
+ if (retval == SI_LOOP) {
+ self->channels_looped++;
+ self->channels[chan].state = SI_STATE_PENDING;
+ self->channels[chan].delay = 0;
+
+ if (self->channels_looped == self->active_channels) {
+ int i;
+
+ /* Everyone's ready: Let's loop */
+ for (i = 0; i < self->channels_nr; i++)
+ if (self->channels[i].state
+ == SI_STATE_PENDING)
+ self->channels[i].state
+ = SI_STATE_DELTA_TIME;
+
+ self->channels_looped = 0;
+ return SI_LOOP;
+ }
+ } else if (retval == SI_FINISHED) {
+#ifdef DEBUG
+ fprintf(stderr, "FINISHED some channel\n");
+#endif
+ } else if (retval > 0) {
+ int sd ;
+ sd = _sci1_get_smallest_delta(self);
+
+ if (_sci1_no_delta_time(self) && sd) {
+ /* No other channel is ready */
+ _sci1_update_delta(self, sd);
+
+ /* Only from here do we return delta times */
+ return sd;
+ }
+ }
+
+ } /* Not a PCM */
+
+ } while (retval > 0); /* All delays must be processed separately */
+
+ return retval;
+}
+
+static struct _song_iterator *
+_sci1_handle_message(sci1_song_iterator_t *self,
+ song_iterator_message_t msg)
+{
+ if (msg.recipient == _SIMSG_BASE) { /* May extend this in the future */
+ switch (msg.type) {
+
+ case _SIMSG_BASEMSG_PRINT: {
+ int playmask = 0;
+ int i;
+
+ for (i = 0; i < self->channels_nr; i++)
+ playmask |= self->channels[i].playmask;
+
+ print_tabs_id(msg.args[0].i, self->ID);
+ fprintf(stderr, "SCI1: chan-nr=%d, playmask=%04x\n",
+ self->channels_nr, playmask);
+ }
+ break;
+
+ case _SIMSG_BASEMSG_CLONE: {
+ int tsize = sizeof(sci1_song_iterator_t);
+ sci1_song_iterator_t *mem = (sci1_song_iterator_t*)sci_malloc(tsize);
+ sci1_sample_t **samplep;
+ int delta = msg.args[0].i; /* Delay until next step */
+
+ memcpy(mem, self, tsize);
+ samplep = &(mem->next_sample);
+
+ sci_refcount_incref(mem->data);
+
+ mem->delay_remaining += delta;
+
+ /* Clone chain of samples */
+ while (*samplep) {
+ sci1_sample_t *newsample
+ = (sci1_sample_t*)sci_malloc(sizeof(sci1_sample_t));
+ memcpy(newsample, *samplep,
+ sizeof(sci1_sample_t));
+ *samplep = newsample;
+ samplep = &(newsample->next);
+ }
+
+ return (struct _song_iterator *) mem; /* Assume caller has another copy of this */
+ }
+
+ case _SIMSG_BASEMSG_STOP: {
+ songit_id_t sought_id = msg.ID;
+ int i;
+
+ if (sought_id == self->ID) {
+ self->ID = 0;
+
+ for (i = 0; i < self->channels_nr; i++)
+ self->channels[i].state = SI_STATE_FINISHED;
+ }
+ break;
+ }
+
+ case _SIMSG_BASEMSG_SET_PLAYMASK: if (msg.ID == self->ID) {
+ self->channel_mask = 0;
+
+ self->device_id
+ = sci0_to_sci1_device_map
+ [sci_ffs(msg.args[0].i & 0xff) - 1]
+ [sfx_pcm_available()]
+ ;
+
+ if (self->device_id == 0xff) {
+ sciprintf("[iterator-1] Warning: Device %d(%d) not supported",
+ msg.args[0].i & 0xff, sfx_pcm_available());
+ }
+ if (self->initialised) {
+ int i;
+ int toffset = -1;
+
+ for (i = 0; i < self->channels_nr; i++)
+ if (self->channels[i].state != SI_STATE_FINISHED
+ && self->channels[i].total_timepos > toffset) {
+ toffset = self->channels[i].total_timepos
+ + self->channels[i].timepos_increment
+ - self->channels[i].delay;
+ }
+
+ /* Find an active channel so that we can
+ ** get the correct time offset */
+
+ _sci1_song_init(self);
+
+ toffset -= self->delay_remaining;
+ self->delay_remaining = 0;
+
+ if (toffset > 0)
+ return new_fast_forward_iterator((song_iterator_t *) self,
+ toffset);
+ } else {
+ _sci1_song_init(self);
+ self->initialised = 1;
+ }
+
+ break;
+
+ }
+
+ case _SIMSG_BASEMSG_SET_LOOPS:
+ if (msg.ID == self->ID)
+ self->loops = (msg.args[0].i > 32767)? 99 : 0;
+ /* 99 is arbitrary, but we can't use '1' because of
+ ** the way we're testing in the decoding section. */
+ break;
+
+ case _SIMSG_BASEMSG_SET_HOLD:
+ self->hold = msg.args[0].i;
+ break;
+ case _SIMSG_BASEMSG_SET_RHYTHM:
+ /* Ignore */
+ break;
+
+ case _SIMSG_BASEMSG_SET_FADE:
+ {
+ fade_params_t *fp = (fade_params_t *) msg.args[0].p;
+ self->fade.action = fp->action;
+ self->fade.final_volume = fp->final_volume;
+ self->fade.ticks_per_step = fp->ticks_per_step;
+ self->fade.step_size = fp->step_size;
+ break;
+ }
+
+ default:
+ fprintf(stderr, SIPFX "Unsupported command %d to"
+ " SCI1 iterator", msg.type);
+ }
+ return (song_iterator_t *) self;
+ }
+ return NULL;
+}
+
+
+static int
+_sci1_read_next_command(sci1_song_iterator_t *self,
+ unsigned char *buf, int *result)
+{
+ return _sci1_process_next_command(self, buf, result);
+}
+
+
+static void
+_sci1_init(sci1_song_iterator_t *self)
+{
+ _common_init((base_song_iterator_t *) self);
+ self->ccc = 127;
+ self->device_id = 0x00; /* Default to Sound Blaster/Adlib for purposes
+ ** of cue computation */
+ self->next_sample = NULL;
+ self->channels_nr = 0;
+ self->initialised = 0;
+ self->delay_remaining = 0;
+ self->loops = 0;
+ self->hold = 0;
+ memset(self->polyphony, 0, sizeof(self->polyphony));
+ memset(self->importance, 0, sizeof(self->importance));
+}
+
+static void
+_sci1_cleanup(sci1_song_iterator_t *it)
+{
+ sci1_sample_t *sample_seeker = it->next_sample;
+ while (sample_seeker) {
+ sci1_sample_t *old_sample = sample_seeker;
+ sample_seeker = sample_seeker->next;
+ sci_free(old_sample);
+ }
+
+ _sci0_cleanup((sci0_song_iterator_t *)it);
+}
+
+static int
+_sci1_get_timepos(sci1_song_iterator_t *self)
+{
+ int max = 0;
+ int i;
+
+ for (i = 0; i < self->channels_nr; i++)
+ if (self->channels[i].total_timepos > max)
+ max = self->channels[i].total_timepos;
+
+ return max;
+}
+
+/*****************************/
+/*-- Cleanup song iterator --*/
+/*****************************/
+
+
+static void
+_cleanup_iterator_init(song_iterator_t *it)
+{
+}
+
+static song_iterator_t *
+_cleanup_iterator_handle_message(song_iterator_t *i, song_iterator_message_t msg)
+{
+ if (msg.recipient == _SIMSG_BASEMSG_PRINT
+ && msg.type == _SIMSG_BASEMSG_PRINT) {
+ print_tabs_id(msg.args[0].i, i->ID);
+ fprintf(stderr, "CLEANUP\n");
+ }
+
+ return NULL;
+}
+
+static int
+_cleanup_iterator_next(song_iterator_t *self, unsigned char *buf, int *result)
+{
+ /* Task: Return channel-notes-off for each channel */
+ if (self->channel_mask) {
+ int bs = sci_ffs(self->channel_mask) - 1;
+
+ self->channel_mask &= ~(1 << bs);
+ buf[0] = 0xb0 | bs; /* Controller */
+ buf[1] = SCI_MIDI_CHANNEL_NOTES_OFF;
+ buf[2] = 0; /* Hmm... */
+ *result = 3;
+ return 0;
+ } else
+ return SI_FINISHED;
+}
+
+song_iterator_t *
+new_cleanup_iterator(unsigned int channels)
+{
+ song_iterator_t *it = (song_iterator_t*)sci_malloc(sizeof(song_iterator_t));
+ it->channel_mask = channels;
+ it->ID = 17;
+ it->flags = 0;
+ it->death_listeners_nr = 0;
+
+ it->cleanup = NULL;
+ it->get_pcm_feed = NULL;
+ it->init = _cleanup_iterator_init;
+ it->handle_message = _cleanup_iterator_handle_message;
+ it->get_timepos = NULL;
+ it->next = _cleanup_iterator_next;
+ return it;
+}
+
+/**********************************/
+/*-- Fast-forward song iterator --*/
+/**********************************/
+
+static int
+_ff_read_next_command(fast_forward_song_iterator_t *self,
+ byte *buf, int *result)
+{
+ int rv;
+
+ if (self->delta <= 0)
+ return SI_MORPH; /* Did our duty */
+
+ while (1) {
+ rv = self->delegate->next(self->delegate, buf, result);
+
+ if (rv > 0) {
+ /* Subtract from the delta we want to wait */
+ self->delta -= rv;
+
+ /* Done */
+ if (self->delta < 0)
+ return -self->delta;
+ }
+
+ if (rv <= 0)
+ return rv;
+ }
+}
+
+static sfx_pcm_feed_t *
+_ff_check_pcm(fast_forward_song_iterator_t *self)
+{
+ return self->delegate->get_pcm_feed(self->delegate);
+}
+
+static song_iterator_t *
+_ff_handle_message(fast_forward_song_iterator_t *self,
+ song_iterator_message_t msg)
+{
+ if (msg.recipient == _SIMSG_PLASTICWRAP)
+ switch (msg.type) {
+
+ case _SIMSG_PLASTICWRAP_ACK_MORPH:
+ if (self->delta <= 0) {
+ song_iterator_t *it = self->delegate;
+ sci_free(self);
+ return it;
+ }
+ break;
+
+ default:
+ BREAKPOINT();
+ }
+ else if (msg.recipient == _SIMSG_BASE) {
+ switch (msg.type) {
+
+ case _SIMSG_BASEMSG_CLONE: {
+ int tsize = sizeof(fast_forward_song_iterator_t);
+ fast_forward_song_iterator_t *clone = (fast_forward_song_iterator_t *)sci_malloc(tsize);
+ memcpy(clone, self, tsize);
+ songit_handle_message(&clone->delegate, msg);
+ return (song_iterator_t *) clone;
+ }
+
+ case _SIMSG_BASEMSG_PRINT:
+ print_tabs_id(msg.args[0].i, self->ID);
+ fprintf(stderr, "PLASTICWRAP:\n");
+ msg.args[0].i++;
+ songit_handle_message(&(self->delegate), msg);
+ break;
+
+ default:
+ songit_handle_message(&(self->delegate), msg);
+ }
+ } else
+ songit_handle_message(&(self->delegate), msg);
+
+ return NULL;
+}
+
+
+static void
+_ff_init(fast_forward_song_iterator_t *self)
+{
+ return;
+}
+
+static int
+_ff_get_timepos(fast_forward_song_iterator_t *self)
+{
+ return self->delegate->get_timepos(self->delegate);
+}
+
+song_iterator_t *
+new_fast_forward_iterator(song_iterator_t *capsit, int delta)
+{
+ fast_forward_song_iterator_t *it =
+ (fast_forward_song_iterator_t*)sci_malloc(sizeof(fast_forward_song_iterator_t));
+
+ if (capsit == NULL)
+ {
+ free(it);
+ return NULL;
+ }
+ it->ID = 0;
+
+ it->delegate = capsit;
+ it->delta = delta;
+ it->death_listeners_nr = 0;
+
+ it->next = (int(*)(song_iterator_t *, unsigned char *, int *))
+ _ff_read_next_command;
+ it->get_pcm_feed = (sfx_pcm_feed_t *(*)(song_iterator_t *))
+ _ff_check_pcm;
+ it->handle_message = (song_iterator_t *(*)(song_iterator_t *,
+ song_iterator_message_t))
+ _ff_handle_message;
+ it->get_timepos = (int(*)(song_iterator_t *))_ff_get_timepos;
+ it->init = (void(*)(song_iterator_t *))
+ _ff_init;
+ it->cleanup = NULL;
+ it->channel_mask = capsit->channel_mask;
+
+
+ return (song_iterator_t *) it;
+}
+
+
+/********************/
+/*-- Tee iterator --*/
+/********************/
+
+
+static int
+_tee_read_next_command(tee_song_iterator_t *it, unsigned char *buf,
+ int *result)
+{
+ static int ready_masks[2] = {TEE_LEFT_READY, TEE_RIGHT_READY};
+ static int active_masks[2] = {TEE_LEFT_ACTIVE, TEE_RIGHT_ACTIVE};
+ static int pcm_masks[2] = {TEE_LEFT_PCM, TEE_RIGHT_PCM};
+ int i;
+ int retid;
+
+#ifdef DEBUG_TEE_ITERATOR
+ fprintf(stderr, "[Tee] %02x\n", it->status);
+#endif
+
+ if (!(it->status & (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE)))
+ /* None is active? */
+ return SI_FINISHED;
+
+ if (it->morph_deferred == TEE_MORPH_READY)
+ return SI_MORPH;
+
+ if ((it->status & (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE))
+ != (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE)) {
+ /* Not all are is active? */
+ int which;
+#ifdef DEBUG_TEE_ITERATOR
+ fprintf(stderr, "\tRequesting transformation...\n");
+#endif
+ if (it->status & TEE_LEFT_ACTIVE)
+ which = TEE_LEFT;
+ else if (it->status & TEE_RIGHT_ACTIVE)
+ which = TEE_RIGHT;
+ memcpy(buf, it->children[which].buf, MAX_BUF_SIZE);
+ *result = it->children[which].result;
+ it->morph_deferred = TEE_MORPH_READY;
+ return it->children[which].retval;
+ }
+
+ /* First, check for unreported PCMs */
+ for (i = TEE_LEFT; i <= TEE_RIGHT; i++)
+ if ((it->status & (ready_masks[i] | pcm_masks[i]))
+ == (ready_masks[i] | pcm_masks[i])) {
+ it->status &= ~ready_masks[i];
+ return SI_PCM;
+ }
+
+ for (i = TEE_LEFT; i <= TEE_RIGHT; i++)
+ if (!(it->status & ready_masks[i])) {
+
+ /* Buffers aren't ready yet */
+ it->children[i].retval =
+ songit_next(&(it->children[i].it),
+ it->children[i].buf,
+ &(it->children[i].result),
+ IT_READER_MASK_ALL
+ | IT_READER_MAY_FREE
+ | IT_READER_MAY_CLEAN);
+
+ it->status |= ready_masks[i];
+#ifdef DEBUG_TEE_ITERATOR
+ fprintf(stderr, "\t Must check %d: %d\n", i,
+ it->children[i].retval);
+#endif
+
+ if (it->children[i].retval == SI_ABSOLUTE_CUE ||
+ it->children[i].retval == SI_RELATIVE_CUE)
+ return it->children[i].retval;
+ if (it->children[i].retval == SI_FINISHED) {
+ it->status &= ~active_masks[i];
+ /* Recurse to complete */
+#ifdef DEBUG_TEE_ITERATOR
+fprintf(stderr, "\t Child %d signalled completion, recursing w/ status %02x\n", i, it->status);
+#endif
+ return _tee_read_next_command(it, buf, result);
+ } else if (it->children[i].retval == SI_PCM) {
+ it->status |= pcm_masks[i];
+ it->status &= ~ready_masks[i];
+ return SI_PCM;
+ }
+ }
+
+
+ /* We've already handled PCM, MORPH and FINISHED, CUEs & LOOP remain */
+
+ retid = TEE_LEFT;
+ if ((it->children[TEE_LEFT].retval > 0)
+ /* Asked to delay */
+ && (it->children[TEE_RIGHT].retval <= it->children[TEE_LEFT].retval))
+ /* Is not delaying or not delaying as much */
+ retid = TEE_RIGHT;
+
+#ifdef DEBUG_TEE_ITERATOR
+fprintf(stderr, "\tl:%d / r:%d / chose %d\n",
+ it->children[TEE_LEFT].retval, it->children[TEE_RIGHT].retval, retid);
+#endif
+#if 0
+ if (it->children[retid].retval == 0) {
+ /* Perform remapping, if neccessary */
+ byte *buf = it->children[retid].buf;
+ if (*buf != SCI_MIDI_SET_SIGNAL
+ && *buf < 0xf0) { /* Not a generic command */
+ int chan = *buf & 0xf;
+ int op = *buf & 0xf0;
+
+ chan = it->children[retid].channel_remap[chan];
+
+ *buf = chan | op;
+ }
+ }
+#endif
+
+ /* Adjust delta times */
+ if (it->children[retid].retval > 0
+ && it->children[1-retid].retval > 0) {
+ if (it->children[1-retid].retval
+ == it->children[retid].retval)
+ /* If both children wait the same amount of time,
+ ** we have to re-fetch commands from both */
+ it->status &= ~ready_masks[1-retid];
+ else
+ /* If they don't, we can/must re-use the other
+ ** child's delay time */
+ it->children[1-retid].retval
+ -= it->children[retid].retval;
+ }
+
+ it->status &= ~ready_masks[retid];
+ memcpy(buf, it->children[retid].buf, MAX_BUF_SIZE);
+ *result = it->children[retid].result;
+
+ return it->children[retid].retval;
+}
+
+static sfx_pcm_feed_t *
+_tee_check_pcm(tee_song_iterator_t *it)
+{
+ static int pcm_masks[2] = {TEE_LEFT_PCM, TEE_RIGHT_PCM};
+ int i;
+
+ for (i = TEE_LEFT; i <= TEE_RIGHT; i++)
+ if (it->status & pcm_masks[i]) {
+
+ it->status &= ~pcm_masks[i];
+ return it->children[i].it->
+ get_pcm_feed(it->children[i].it);
+ }
+
+ return NULL; /* No iterator */
+}
+
+static song_iterator_t *
+_tee_handle_message(tee_song_iterator_t *self, song_iterator_message_t msg)
+{
+ if (msg.recipient == _SIMSG_BASE) {
+ switch (msg.type) {
+
+ case _SIMSG_BASEMSG_PRINT:
+ print_tabs_id(msg.args[0].i, self->ID);
+ fprintf(stderr, "TEE:\n");
+ msg.args[0].i++;
+ break; /* And continue with our children */
+
+ case _SIMSG_BASEMSG_CLONE: {
+ tee_song_iterator_t *newit
+ = (tee_song_iterator_t*)sci_malloc(sizeof(tee_song_iterator_t));
+ memcpy(newit, self, sizeof(tee_song_iterator_t));
+
+ if (newit->children[TEE_LEFT].it)
+ newit->children[TEE_LEFT].it =
+ songit_clone(newit->children[TEE_LEFT].it, msg.args[0].i);
+ if (newit->children[TEE_RIGHT].it)
+ newit->children[TEE_RIGHT].it =
+ songit_clone(newit->children[TEE_RIGHT].it, msg.args[0].i);
+
+ return (song_iterator_t *) newit;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ if (msg.recipient == _SIMSG_PLASTICWRAP) {
+ song_iterator_t *old_it;
+ switch (msg.type) {
+
+ case _SIMSG_PLASTICWRAP_ACK_MORPH:
+ if (!(self->status & (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE))) {
+ songit_free((song_iterator_t *) self);
+ return NULL;
+ } else if (!(self->status & TEE_LEFT_ACTIVE)) {
+ if (self->may_destroy)
+ songit_free(self->children[TEE_LEFT].it);
+ old_it = self->children[TEE_RIGHT].it;
+ sci_free(self);
+ return old_it;
+ } else if (!(self->status & TEE_RIGHT_ACTIVE)) {
+ if (self->may_destroy)
+ songit_free(self->children[TEE_RIGHT].it);
+ old_it = self->children[TEE_LEFT].it;
+ sci_free(self);
+ return old_it;
+ } else {
+ sciprintf("[tee-iterator] WARNING:"
+ " Morphing without need\n");
+ return (song_iterator_t *) self;
+ }
+
+ default:
+ BREAKPOINT();
+ }
+ }
+
+ if (self->children[TEE_LEFT].it)
+ songit_handle_message(&(self->children[TEE_LEFT].it), msg);
+ if (self->children[TEE_RIGHT].it)
+ songit_handle_message(&(self->children[TEE_RIGHT].it), msg);
+
+ return NULL;
+}
+
+static void
+_tee_init(tee_song_iterator_t *it)
+{
+ it->status = TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE;
+ it->children[TEE_LEFT].it->init(it->children[TEE_LEFT].it);
+ it->children[TEE_RIGHT].it->init(it->children[TEE_RIGHT].it);
+}
+
+static void
+_tee_free(tee_song_iterator_t *it)
+{
+ int i;
+ for (i = TEE_LEFT; i <= TEE_RIGHT; i++)
+ if (it->children[i].it && it->may_destroy)
+ songit_free(it->children[i].it);
+}
+
+static void
+songit_tee_death_notification(tee_song_iterator_t *self,
+ song_iterator_t *corpse)
+{
+ if (corpse == self->children[TEE_LEFT].it) {
+ self->status &= ~TEE_LEFT_ACTIVE;
+ self->children[TEE_LEFT].it = NULL;
+ } else if (corpse == self->children[TEE_RIGHT].it) {
+ self->status &= ~TEE_RIGHT_ACTIVE;
+ self->children[TEE_RIGHT].it = NULL;
+ } else {
+ BREAKPOINT();
+ }
+}
+
+
+song_iterator_t *
+songit_new_tee(song_iterator_t *left, song_iterator_t *right, int may_destroy)
+{
+ int i;
+ int firstfree = 1; /* First free channel */
+ int incomplete_map = 0;
+ tee_song_iterator_t *it = (tee_song_iterator_t*)sci_malloc(sizeof(tee_song_iterator_t));
+
+ it->ID = 0;
+
+ it->morph_deferred = TEE_MORPH_NONE;
+ it->status = TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE;
+ it->may_destroy = may_destroy;
+
+ it->children[TEE_LEFT].it = left;
+ it->children[TEE_RIGHT].it = right;
+ it->death_listeners_nr = 0;
+
+ /* By default, don't remap */
+ for (i = 0; i < 16; i++)
+ it->children[TEE_LEFT].channel_remap[i]
+ = it->children[TEE_RIGHT].channel_remap[i] = i;
+
+ /* Default to lhs channels */
+ it->channel_mask = left->channel_mask;
+ for (i = 0; i < 16; i++)
+ if (it->channel_mask & (1 << i) & right->channel_mask
+ && (i != MIDI_RHYTHM_CHANNEL) /* Share rhythm */) { /*conflict*/
+ while ((firstfree == MIDI_RHYTHM_CHANNEL)
+ /* Either if it's the rhythm channel or if it's taken */
+ || (firstfree < MIDI_CHANNELS
+ && ((1 << firstfree) & it->channel_mask)))
+ ++firstfree;
+
+ if (firstfree == MIDI_CHANNELS) {
+ incomplete_map = 1;
+ fprintf(stderr, "[songit-tee <%08lx,%08lx>] "
+ "Could not remap right channel #%d:"
+ " Out of channels\n",
+ left->ID, right->ID, i);
+ } else {
+ it->children[TEE_RIGHT].channel_remap[i]
+ = firstfree;
+
+ it->channel_mask |= (1 << firstfree);
+ }
+ }
+#ifdef DEBUG_TEE_ITERATOR
+ if (incomplete_map) {
+ int c;
+ fprintf(stderr, "[songit-tee <%08lx,%08lx>] Channels:"
+ " %04x <- %04x | %04x\n",
+ left->ID, right->ID,
+ it->channel_mask,
+ left->channel_mask, right->channel_mask);
+ for (c =0 ; c < 2; c++)
+ for (i =0 ; i < 16; i++)
+ fprintf(stderr, " map [%d][%d] -> %d\n",
+ c, i, it->children[c].channel_remap[i]);
+ }
+#endif
+
+
+ it->next = (int(*)(song_iterator_t *, unsigned char *, int *))
+ _tee_read_next_command;
+
+ it->get_pcm_feed = (sfx_pcm_feed_t*(*)(song_iterator_t *))
+ _tee_check_pcm;
+
+ it->handle_message = (song_iterator_t *(*)(song_iterator_t *,
+ song_iterator_message_t))
+ _tee_handle_message;
+
+ it->init = (void(*)(song_iterator_t *))
+ _tee_init;
+
+ it->get_timepos = NULL;
+
+ song_iterator_add_death_listener((song_iterator_t *)it,
+ left, (void (*)(void *, void*))
+ songit_tee_death_notification);
+ song_iterator_add_death_listener((song_iterator_t *)it,
+ right, (void (*)(void *, void*))
+ songit_tee_death_notification);
+
+ it->cleanup = NULL;
+
+ return (song_iterator_t *) it;
+}
+
+
+/*************************************/
+/*-- General purpose functionality --*/
+/*************************************/
+
+int
+songit_next(song_iterator_t **it, unsigned char *buf, int *result, int mask)
+{
+ int retval;
+
+ if (!*it)
+ return SI_FINISHED;
+
+ do {
+ retval = (*it)->next(*it, buf, result);
+ if (retval == SI_MORPH) {
+ fprintf(stderr, " Morphing %p (stored at %p)\n", *it, it);
+ if (!SIMSG_SEND((*it), SIMSG_ACK_MORPH)) {
+ BREAKPOINT();
+ } else fprintf(stderr, "SI_MORPH successful\n");
+ }
+
+ if (retval == SI_FINISHED)
+ fprintf(stderr, "[song-iterator] Song finished. mask = %04x, cm=%04x\n",
+ mask, (*it)->channel_mask);
+ if (retval == SI_FINISHED
+ && (mask & IT_READER_MAY_CLEAN)
+ && (*it)->channel_mask) { /* This last test will fail
+ ** with a terminated
+ ** cleanup iterator */
+ int channel_mask = (*it)->channel_mask;
+
+ if (mask & IT_READER_MAY_FREE)
+ songit_free(*it);
+ *it = new_cleanup_iterator(channel_mask);
+ retval = -9999; /* Continue */
+ }
+ } while (! ( /* Until one of the following holds */
+ (retval > 0 && (mask & IT_READER_MASK_DELAY))
+ || (retval == 0 && (mask & IT_READER_MASK_MIDI))
+ || (retval == SI_LOOP && (mask & IT_READER_MASK_LOOP))
+ || (retval == SI_ABSOLUTE_CUE &&
+ (mask & IT_READER_MASK_CUE))
+ || (retval == SI_RELATIVE_CUE &&
+ (mask & IT_READER_MASK_CUE))
+ || (retval == SI_PCM && (mask & IT_READER_MASK_PCM))
+ || (retval == SI_FINISHED)
+ ));
+
+ if (retval == SI_FINISHED
+ && (mask & IT_READER_MAY_FREE)) {
+ songit_free(*it);
+ *it = NULL;
+ }
+
+ return retval;
+}
+
+
+
+song_iterator_t *
+songit_new(unsigned char *data, unsigned int size, int type, songit_id_t id)
+{
+ base_song_iterator_t *it;
+ int i;
+
+ if (!data || size < 22) {
+ fprintf(stderr, SIPFX "Attempt to instantiate song iterator for null"
+ " song data\n");
+ return NULL;
+ }
+
+
+ switch (type) {
+
+ case SCI_SONG_ITERATOR_TYPE_SCI0:
+ /**-- Playing SCI0 sound resources --**/
+ it = (base_song_iterator_t*)sci_malloc(sizeof(sci0_song_iterator_t));
+ it->channel_mask = 0xffff; /* Allocate all channels by default */
+
+ for (i = 0; i < MIDI_CHANNELS; i++)
+ it->polyphony[i] = data[1 + (i << 1)];
+
+ it->next = (int(*)(song_iterator_t *, unsigned char *, int *))
+ _sci0_read_next_command;
+ it->get_pcm_feed = (sfx_pcm_feed_t*(*)(song_iterator_t *))
+ _sci0_check_pcm;
+ it->handle_message = (song_iterator_t *(*)(song_iterator_t *, song_iterator_message_t))
+ _sci0_handle_message;
+ it->init = (void(*)(song_iterator_t *))_sci0_init;
+ it->cleanup = (void(*)(song_iterator_t *))_sci0_cleanup;
+ ((sci0_song_iterator_t *)it)->channel.state
+ = SI_STATE_UNINITIALISED;
+ it->get_timepos = (int(*)(song_iterator_t *))_sci0_get_timepos;
+ break;
+
+ case SCI_SONG_ITERATOR_TYPE_SCI1:
+ /**-- SCI01 or later sound resource --**/
+ it = (base_song_iterator_t*)sci_malloc(sizeof(sci1_song_iterator_t));
+ it->channel_mask = 0; /* Defer channel allocation */
+
+ for (i = 0; i < MIDI_CHANNELS; i++)
+ it->polyphony[i] = 0; /* Unknown */
+
+ it->next = (int(*)(song_iterator_t *, unsigned char *, int *))
+ _sci1_read_next_command;
+ it->get_pcm_feed = (sfx_pcm_feed_t*(*)(song_iterator_t *))
+ _sci1_get_pcm;
+ it->handle_message = (song_iterator_t *(*)(song_iterator_t *, song_iterator_message_t))
+ _sci1_handle_message;
+ it->init = (void(*)(song_iterator_t *))_sci1_init;
+ it->cleanup = (void(*)(song_iterator_t *))_sci1_cleanup;
+ it->get_timepos = (int(*)(song_iterator_t *))_sci1_get_timepos;
+ break;
+
+ default:
+ /**-- Invalid/unsupported sound resources --**/
+ fprintf(stderr, SIPFX "Attempt to instantiate invalid/unknown"
+ " song iterator type %d\n", type);
+ return NULL;
+ }
+ it->ID = id;
+
+ it->death_listeners_nr = 0;
+
+ it->data = (unsigned char*)sci_refcount_memdup(data, size);
+ it->size = size;
+
+ it->init((song_iterator_t *) it);
+
+ return (song_iterator_t *) it;
+}
+
+void
+songit_free(song_iterator_t *it)
+{
+ if (it) {
+ int i;
+
+ if (it->cleanup)
+ it->cleanup(it);
+
+ for (i = 0; i < it->death_listeners_nr; i++)
+ it->death_listeners[i].notify(it->death_listeners[i].self, it);
+
+ sci_free(it);
+ }
+}
+
+song_iterator_message_t
+songit_make_message(songit_id_t id, int recipient, int type, int a1, int a2)
+{
+ song_iterator_message_t rv;
+ rv.ID = id;
+ rv.recipient = recipient;
+ rv.type = type;
+ rv.args[0].i = a1;
+ rv.args[1].i = a2;
+
+ return rv;
+}
+
+song_iterator_message_t
+songit_make_ptr_message(songit_id_t id, int recipient, int type, void * a1, int a2)
+{
+ song_iterator_message_t rv;
+ rv.ID = id;
+ rv.recipient = recipient;
+ rv.type = type;
+ rv.args[0].p = a1;
+ rv.args[1].i = a2;
+
+ return rv;
+}
+
+
+int
+songit_handle_message(song_iterator_t **it_reg_p, song_iterator_message_t msg)
+{
+ song_iterator_t *it = *it_reg_p;
+ song_iterator_t *newit;
+
+ newit = it->handle_message(it, msg);
+
+ if (!newit)
+ return 0; /* Couldn't handle */
+
+ *it_reg_p = newit; /* Might have self-morphed */
+ return 1;
+}
+
+song_iterator_t *
+songit_clone(song_iterator_t *it, int delta)
+{
+ SIMSG_SEND(it, SIMSG_CLONE(delta));
+ it->death_listeners_nr = 0;
+ it->flags |= SONGIT_FLAG_CLONE;
+ return it;
+}
+
+void
+song_iterator_add_death_listener(song_iterator_t *it,
+ void *client,
+ void (*notify) (void *self, void *notifier))
+{
+ if (it->death_listeners_nr >= SONGIT_MAX_LISTENERS) {
+ fprintf(stderr, "FATAL: Too many death listeners for song"
+ " iterator\n");
+ BREAKPOINT();
+ exit(1);
+ }
+
+ it->death_listeners[it->death_listeners_nr].notify = notify;
+ it->death_listeners[it->death_listeners_nr].self = client;
+
+ it->death_listeners_nr++;
+}
+
+void
+song_iterator_remove_death_listener(song_iterator_t *it,
+ void *client)
+{
+ int i;
+ for (i = 0; i < it->death_listeners_nr; i++) {
+ if (it->death_listeners[i].self == client) {
+ --it->death_listeners_nr;
+
+ /* Overwrite, if this wasn't the last one */
+ if (i+1 < it->death_listeners_nr)
+ it->death_listeners[i]
+ = it->death_listeners[it->death_listeners_nr];
+
+ return;
+ }
+ }
+
+ fprintf(stderr, "FATAL: Could not remove death listener from "
+ "song iterator\n");
+ BREAKPOINT();
+ exit(1);
+}
+
+
+song_iterator_t *
+sfx_iterator_combine(song_iterator_t *it1, song_iterator_t *it2)
+{
+ if (it1 == NULL)
+ return it2;
+ if (it2 == NULL)
+ return it1;
+
+ /* Both are non-NULL: */
+ return songit_new_tee(it1, it2, 1); /* 'may destroy' */
+}
diff --git a/engines/sci/sfx/lists/GM.txt b/engines/sci/sfx/lists/GM.txt
new file mode 100644
index 0000000000..eea2510447
--- /dev/null
+++ b/engines/sci/sfx/lists/GM.txt
@@ -0,0 +1,177 @@
+/*000 00*/ "Acoustic Grand Piano",
+/*001 01*/ "Bright Acoustic Piano",
+/*002 02*/ "Electric Grand Piano",
+/*003 03*/ "Honky-tonk Piano",
+/*004 04*/ "Electric Piano 1",
+/*005 05*/ "Electric Piano 2",
+/*006 06*/ "Harpsichord",
+/*007 07*/ "Clavinet",
+/*008 08*/ "Celesta",
+/*009 09*/ "Glockenspiel",
+/*010 0A*/ "Music Box",
+/*011 0B*/ "Vibraphone",
+/*012 0C*/ "Marimba",
+/*013 0D*/ "Xylophone",
+/*014 0E*/ "Tubular Bells",
+/*015 0F*/ "Dulcimer",
+/*016 10*/ "Drawbar Organ",
+/*017 11*/ "Percussive Organ",
+/*018 12*/ "Rock Organ",
+/*019 13*/ "Church Organ",
+/*020 14*/ "Reed Organ",
+/*021 15*/ "Accordion",
+/*022 16*/ "Harmonica",
+/*023 17*/ "Tango Accordion",
+/*024 18*/ "Acoustic Guitar (nylon)",
+/*025 19*/ "Acoustic Guitar (steel)",
+/*026 1A*/ "Electric Guitar (jazz)",
+/*027 1B*/ "Electric Guitar (clean)",
+/*028 1C*/ "Electric Guitar (muted)",
+/*029 1D*/ "Overdriven Guitar",
+/*030 1E*/ "Distortion Guitar",
+/*031 1F*/ "Guitar Harmonics",
+/*032 20*/ "Acoustic Bass",
+/*033 21*/ "Electric Bass (finger)",
+/*034 22*/ "Electric Bass (pick)",
+/*035 23*/ "Fretless Bass",
+/*036 24*/ "Slap Bass 1",
+/*037 25*/ "Slap Bass 2",
+/*038 26*/ "Synth Bass 1",
+/*039 27*/ "Synth Bass 2",
+/*040 28*/ "Violin",
+/*041 29*/ "Viola",
+/*042 2A*/ "Cello",
+/*043 2B*/ "Contrabass",
+/*044 2C*/ "Tremolo Strings",
+/*045 2D*/ "Pizzicato Strings",
+/*046 2E*/ "Orchestral Harp",
+/*047 2F*/ "Timpani",
+/*048 30*/ "String Ensemble 1",
+/*049 31*/ "String Ensemble 2",
+/*050 32*/ "SynthStrings 1",
+/*051 33*/ "SynthStrings 2",
+/*052 34*/ "Choir Aahs",
+/*053 35*/ "Voice Oohs",
+/*054 36*/ "Synth Voice",
+/*055 37*/ "Orchestra Hit",
+/*056 38*/ "Trumpet",
+/*057 39*/ "Trombone",
+/*058 3A*/ "Tuba",
+/*059 3B*/ "Muted Trumpet",
+/*060 3C*/ "French Horn",
+/*061 3D*/ "Brass Section",
+/*062 3E*/ "SynthBrass 1",
+/*063 3F*/ "SynthBrass 2",
+
+/*064 40*/ "Soprano Sax",
+/*065 41*/ "Alto Sax",
+/*066 42*/ "Tenor Sax",
+/*067 43*/ "Baritone Sax",
+/*068 44*/ "Oboe",
+/*069 45*/ "English Horn",
+/*070 46*/ "Bassoon",
+/*071 47*/ "Clarinet",
+/*072 48*/ "Piccolo",
+/*073 49*/ "Flute",
+/*074 4A*/ "Recorder",
+/*075 4B*/ "Pan Flute",
+/*076 4C*/ "Blown Bottle",
+/*077 4D*/ "Shakuhachi",
+/*078 4E*/ "Whistle",
+/*079 4F*/ "Ocarina",
+/*080 50*/ "Lead 1 (square)",
+/*081 51*/ "Lead 2 (sawtooth)",
+/*082 52*/ "Lead 3 (calliope)",
+/*083 53*/ "Lead 4 (chiff)",
+/*084 54*/ "Lead 5 (charang)",
+/*085 55*/ "Lead 6 (voice)",
+/*086 56*/ "Lead 7 (fifths)",
+/*087 57*/ "Lead 8 (bass+lead)",
+/*088 58*/ "Pad 1 (new age)",
+/*089 59*/ "Pad 2 (warm)",
+/*090 5A*/ "Pad 3 (polysynth)",
+/*091 5B*/ "Pad 4 (choir)",
+/*092 5C*/ "Pad 5 (bowed)",
+/*093 5D*/ "Pad 6 (metallic)",
+/*094 5E*/ "Pad 7 (halo)",
+/*095 5F*/ "Pad 8 (sweep)",
+/*096 60*/ "FX 1 (rain)",
+/*097 61*/ "FX 2 (soundtrack)",
+/*098 62*/ "FX 3 (crystal)",
+/*099 63*/ "FX 4 (atmosphere)",
+/*100 64*/ "FX 5 (brightness)",
+/*101 65*/ "FX 6 (goblins)",
+/*102 66*/ "FX 7 (echoes)",
+/*103 67*/ "FX 8 (sci-fi)",
+/*104 68*/ "Sitar",
+/*105 69*/ "Banjo",
+/*106 6A*/ "Shamisen",
+/*107 6B*/ "Koto",
+/*108 6C*/ "Kalimba",
+/*109 6D*/ "Bag pipe",
+/*110 6E*/ "Fiddle",
+/*111 6F*/ "Shannai",
+/*112 70*/ "Tinkle Bell",
+/*113 71*/ "Agogo",
+/*114 72*/ "Steel Drums",
+/*115 73*/ "Woodblock",
+/*116 74*/ "Taiko Drum",
+/*117 75*/ "Melodic Tom",
+/*118 76*/ "Synth Drum",
+/*119 77*/ "Reverse Cymbal",
+/*120 78*/ "Guitar Fret Noise",
+/*121 79*/ "Breath Noise",
+/*122 7A*/ "Seashore",
+/*123 7B*/ "Bird Tweet",
+/*124 7C*/ "Telephone Ring",
+/*125 7D*/ "Helicopter",
+/*126 7E*/ "Applause",
+/*127 7F*/ "Gunshot"
+
+/*035 23*/ "Acoustic Bass Drum",
+/*036 24*/ "Bass Drum 1",
+/*037 25*/ "Side Stick",
+/*038 26*/ "Acoustic Snare",
+/*039 27*/ "Hand Clap",
+/*040 28*/ "Electric Snare",
+/*041 29*/ "Low Floor Tom",
+/*042 2A*/ "Closed Hi-Hat",
+/*043 2B*/ "High Floor Tom",
+/*044 2C*/ "Pedal Hi-Hat",
+/*045 2D*/ "Low Tom",
+/*046 2E*/ "Open Hi-Hat",
+/*047 2F*/ "Low-Mid Tom",
+/*048 30*/ "Hi-Mid Tom",
+/*049 31*/ "Crash Cymbal 1",
+/*050 32*/ "High Tom",
+/*051 33*/ "Ride Cymbal 1",
+/*052 34*/ "Chinese Cymbal",
+/*053 35*/ "Ride Bell",
+/*054 36*/ "Tambourine",
+/*055 37*/ "Splash Cymbal",
+/*056 38*/ "Cowbell",
+/*057 39*/ "Crash Cymbal 2",
+/*058 3A*/ "Vibraslap",
+/*059 3B*/ "Ride Cymbal 2",
+/*060 3C*/ "Hi Bongo",
+/*061 3D*/ "Low Bongo",
+/*062 3E*/ "Mute Hi Conga",
+/*063 3F*/ "Open Hi Conga",
+/*064 40*/ "Low Conga",
+/*065 41*/ "High Timbale",
+/*066 42*/ "Low Timbale",
+/*067 43*/ "High Agogo",
+/*068 44*/ "Low Agogo",
+/*069 45*/ "Cabasa",
+/*070 46*/ "Maracas",
+/*071 47*/ "Short Whistle",
+/*072 48*/ "Long Whistle",
+/*073 49*/ "Short Guiro",
+/*074 4A*/ "Long Guiro",
+/*075 4B*/ "Claves",
+/*076 4C*/ "Hi Wood Block",
+/*077 4D*/ "Low Wood Block",
+/*078 4E*/ "Mute Cuica",
+/*079 4F*/ "Open Cuica",
+/*080 50*/ "Mute Triangle",
+/*081 51*/ "Open Triangle"
diff --git a/engines/sci/sfx/lists/gm_patches.c b/engines/sci/sfx/lists/gm_patches.c
new file mode 100644
index 0000000000..b959a87461
--- /dev/null
+++ b/engines/sci/sfx/lists/gm_patches.c
@@ -0,0 +1,198 @@
+/***************************************************************************
+ gm_patches.c Copyright (C) 2000 Rickard Lind
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+char *GM_Patch[128] = {
+/*000*/ "Acoustic Grand Piano",
+/*001*/ "Bright Acoustic Piano",
+/*002*/ "Electric Grand Piano",
+/*003*/ "Honky-tonk Piano",
+/*004*/ "Electric Piano 1",
+/*005*/ "Electric Piano 2",
+/*006*/ "Harpsichord",
+/*007*/ "Clavinet",
+/*008*/ "Celesta",
+/*009*/ "Glockenspiel",
+/*010*/ "Music Box",
+/*011*/ "Vibraphone",
+/*012*/ "Marimba",
+/*013*/ "Xylophone",
+/*014*/ "Tubular Bells",
+/*015*/ "Dulcimer",
+/*016*/ "Drawbar Organ",
+/*017*/ "Percussive Organ",
+/*018*/ "Rock Organ",
+/*019*/ "Church Organ",
+/*020*/ "Reed Organ",
+/*021*/ "Accordion",
+/*022*/ "Harmonica",
+/*023*/ "Tango Accordion",
+/*024*/ "Acoustic Guitar (nylon)",
+/*025*/ "Acoustic Guitar (steel)",
+/*026*/ "Electric Guitar (jazz)",
+/*027*/ "Electric Guitar (clean)",
+/*028*/ "Electric Guitar (muted)",
+/*029*/ "Overdriven Guitar",
+/*030*/ "Distortion Guitar",
+/*031*/ "Guitar Harmonics",
+/*032*/ "Acoustic Bass",
+/*033*/ "Electric Bass (finger)",
+/*034*/ "Electric Bass (pick)",
+/*035*/ "Fretless Bass",
+/*036*/ "Slap Bass 1",
+/*037*/ "Slap Bass 2",
+/*038*/ "Synth Bass 1",
+/*039*/ "Synth Bass 2",
+/*040*/ "Violin",
+/*041*/ "Viola",
+/*042*/ "Cello",
+/*043*/ "Contrabass",
+/*044*/ "Tremolo Strings",
+/*045*/ "Pizzicato Strings",
+/*046*/ "Orchestral Harp",
+/*047*/ "Timpani",
+/*048*/ "String Ensemble 1",
+/*049*/ "String Ensemble 2",
+/*050*/ "SynthStrings 1",
+/*051*/ "SynthStrings 2",
+/*052*/ "Choir Aahs",
+/*053*/ "Voice Oohs",
+/*054*/ "Synth Voice",
+/*055*/ "Orchestra Hit",
+/*056*/ "Trumpet",
+/*057*/ "Trombone",
+/*058*/ "Tuba",
+/*059*/ "Muted Trumpet",
+/*060*/ "French Horn",
+/*061*/ "Brass Section",
+/*062*/ "SynthBrass 1",
+/*063*/ "SynthBrass 2",
+/*064*/ "Soprano Sax",
+/*065*/ "Alto Sax",
+/*066*/ "Tenor Sax",
+/*067*/ "Baritone Sax",
+/*068*/ "Oboe",
+/*069*/ "English Horn",
+/*070*/ "Bassoon",
+/*071*/ "Clarinet",
+/*072*/ "Piccolo",
+/*073*/ "Flute",
+/*074*/ "Recorder",
+/*075*/ "Pan Flute",
+/*076*/ "Blown Bottle",
+/*077*/ "Shakuhachi",
+/*078*/ "Whistle",
+/*079*/ "Ocarina",
+/*080*/ "Lead 1 (square)",
+/*081*/ "Lead 2 (sawtooth)",
+/*082*/ "Lead 3 (calliope)",
+/*083*/ "Lead 4 (chiff)",
+/*084*/ "Lead 5 (charang)",
+/*085*/ "Lead 6 (voice)",
+/*086*/ "Lead 7 (fifths)",
+/*087*/ "Lead 8 (bass+lead)",
+/*088*/ "Pad 1 (new age)",
+/*089*/ "Pad 2 (warm)",
+/*090*/ "Pad 3 (polysynth)",
+/*091*/ "Pad 4 (choir)",
+/*092*/ "Pad 5 (bowed)",
+/*093*/ "Pad 6 (metallic)",
+/*094*/ "Pad 7 (halo)",
+/*095*/ "Pad 8 (sweep)",
+/*096*/ "FX 1 (rain)",
+/*097*/ "FX 2 (soundtrack)",
+/*098*/ "FX 3 (crystal)",
+/*099*/ "FX 4 (atmosphere)",
+/*100*/ "FX 5 (brightness)",
+/*101*/ "FX 6 (goblins)",
+/*102*/ "FX 7 (echoes)",
+/*103*/ "FX 8 (sci-fi)",
+/*104*/ "Sitar",
+/*105*/ "Banjo",
+/*106*/ "Shamisen",
+/*107*/ "Koto",
+/*108*/ "Kalimba",
+/*109*/ "Bag pipe",
+/*110*/ "Fiddle",
+/*111*/ "Shannai",
+/*112*/ "Tinkle Bell",
+/*113*/ "Agogo",
+/*114*/ "Steel Drums",
+/*115*/ "Woodblock",
+/*116*/ "Taiko Drum",
+/*117*/ "Melodic Tom",
+/*118*/ "Synth Drum",
+/*119*/ "Reverse Cymbal",
+/*120*/ "Guitar Fret Noise",
+/*121*/ "Breath Noise",
+/*122*/ "Seashore",
+/*123*/ "Bird Tweet",
+/*124*/ "Telephone Ring",
+/*125*/ "Helicopter",
+/*126*/ "Applause",
+/*127*/ "Gunshot" };
+
+char *GM_RhythmKey[47] = {
+/*035*/ "Acoustic Bass Drum",
+/*036*/ "Bass Drum 1",
+/*037*/ "Side Stick",
+/*038*/ "Acoustic Snare",
+/*039*/ "Hand Clap",
+/*040*/ "Electric Snare",
+/*041*/ "Low Floor Tom",
+/*042*/ "Closed Hi-Hat",
+/*043*/ "High Floor Tom",
+/*044*/ "Pedal Hi-Hat",
+/*045*/ "Low Tom",
+/*046*/ "Open Hi-Hat",
+/*047*/ "Low-Mid Tom",
+/*048*/ "Hi-Mid Tom",
+/*049*/ "Crash Cymbal 1",
+/*050*/ "High Tom",
+/*051*/ "Ride Cymbal 1",
+/*052*/ "Chinese Cymbal",
+/*053*/ "Ride Bell",
+/*054*/ "Tambourine",
+/*055*/ "Splash Cymbal",
+/*056*/ "Cowbell",
+/*057*/ "Crash Cymbal 2",
+/*058*/ "Vibraslap",
+/*059*/ "Ride Cymbal 2",
+/*060*/ "Hi Bongo",
+/*061*/ "Low Bongo",
+/*062*/ "Mute Hi Conga",
+/*063*/ "Open Hi Conga",
+/*064*/ "Low Conga",
+/*065*/ "High Timbale",
+/*066*/ "Low Timbale",
+/*067*/ "High Agogo",
+/*068*/ "Low Agogo",
+/*069*/ "Cabasa",
+/*070*/ "Maracas",
+/*071*/ "Short Whistle",
+/*072*/ "Long Whistle",
+/*073*/ "Short Guiro",
+/*074*/ "Long Guiro",
+/*075*/ "Claves",
+/*076*/ "Hi Wood Block",
+/*077*/ "Low Wood Block",
+/*078*/ "Mute Cuica",
+/*079*/ "Open Cuica",
+/*080*/ "Mute Triangle"
+/*081*/ "Open Triangle" };
diff --git a/engines/sci/sfx/lists/mt32_timbres.c b/engines/sci/sfx/lists/mt32_timbres.c
new file mode 100644
index 0000000000..776ff5dbb7
--- /dev/null
+++ b/engines/sci/sfx/lists/mt32_timbres.c
@@ -0,0 +1,181 @@
+/***************************************************************************
+ mt_32_timbres.c Copyright (C) 2000 Rickard Lind
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+char *MT32_Timbre[128] = {
+/*000*/ "AcouPiano1",
+/*001*/ "AcouPiano2",
+/*002*/ "AcouPiano3",
+/*003*/ "ElecPiano1",
+/*004*/ "ElecPiano2",
+/*005*/ "ElecPiano3",
+/*006*/ "ElecPiano4",
+/*007*/ "Honkytonk ",
+/*008*/ "Elec Org 1",
+/*009*/ "Elec Org 2",
+/*010*/ "Elec Org 3",
+/*011*/ "Elec Org 4",
+/*012*/ "Pipe Org 1",
+/*013*/ "Pipe Org 2",
+/*014*/ "Pipe Org 3",
+/*015*/ "Accordion ",
+/*016*/ "Harpsi 1 ",
+/*017*/ "Harpsi 2 ",
+/*018*/ "Harpsi 3 ",
+/*019*/ "Clavi 1 ",
+/*020*/ "Clavi 2 ",
+/*021*/ "Clavi 3 ",
+/*022*/ "Celesta 1 ",
+/*023*/ "Celesta 2 ",
+/*024*/ "Syn Brass1",
+/*025*/ "Syn Brass2",
+/*026*/ "Syn Brass3",
+/*027*/ "Syn Brass4",
+/*028*/ "Syn Bass 1",
+/*029*/ "Syn Bass 2",
+/*030*/ "Syn Bass 3",
+/*031*/ "Syn Bass 4",
+/*032*/ "Fantasy ",
+/*033*/ "Harmo Pan ",
+/*034*/ "Chorale ",
+/*035*/ "Glasses ",
+/*036*/ "Soundtrack",
+/*037*/ "Atmosphere",
+/*038*/ "Warm Bell ",
+/*039*/ "Funny Vox ",
+/*040*/ "Echo Bell ",
+/*041*/ "Ice Rain ",
+/*042*/ "Oboe 2001 ",
+/*043*/ "Echo Pan ",
+/*044*/ "DoctorSolo",
+/*045*/ "Schooldaze",
+/*046*/ "BellSinger",
+/*047*/ "SquareWave",
+/*048*/ "Str Sect 1",
+/*049*/ "Str Sect 2",
+/*050*/ "Str Sect 3",
+/*051*/ "Pizzicato ",
+/*052*/ "Violin 1 ",
+/*053*/ "Violin 2 ",
+/*054*/ "Cello 1 ",
+/*055*/ "Cello 2 ",
+/*056*/ "Contrabass",
+/*057*/ "Harp 1 ",
+/*058*/ "Harp 2 ",
+/*059*/ "Guitar 1 ",
+/*060*/ "Guitar 2 ",
+/*061*/ "Elec Gtr 1",
+/*062*/ "Elec Gtr 2",
+/*063*/ "Sitar ",
+/*064*/ "Acou Bass1",
+/*065*/ "Acou Bass2",
+/*066*/ "Elec Bass1",
+/*067*/ "Elec Bass2",
+/*068*/ "Slap Bass1",
+/*069*/ "Slap Bass2",
+/*070*/ "Fretless 1",
+/*071*/ "Fretless 2",
+/*072*/ "Flute 1 ",
+/*073*/ "Flute 2 ",
+/*074*/ "Piccolo 1 ",
+/*075*/ "Piccolo 2 ",
+/*076*/ "Recorder ",
+/*077*/ "Panpipes ",
+/*078*/ "Sax 1 ",
+/*079*/ "Sax 2 ",
+/*080*/ "Sax 3 ",
+/*081*/ "Sax 4 ",
+/*082*/ "Clarinet 1",
+/*083*/ "Clarinet 2",
+/*084*/ "Oboe ",
+/*085*/ "Engl Horn ",
+/*086*/ "Bassoon ",
+/*087*/ "Harmonica ",
+/*088*/ "Trumpet 1 ",
+/*089*/ "Trumpet 2 ",
+/*090*/ "Trombone 1",
+/*091*/ "Trombone 2",
+/*092*/ "Fr Horn 1 ",
+/*093*/ "Fr Horn 2 ",
+/*094*/ "Tuba ",
+/*095*/ "Brs Sect 1",
+/*096*/ "Brs Sect 2",
+/*097*/ "Vibe 1 ",
+/*098*/ "Vibe 2 ",
+/*099*/ "Syn Mallet",
+/*100*/ "Wind Bell ",
+/*101*/ "Glock ",
+/*102*/ "Tube Bell ",
+/*103*/ "Xylophone ",
+/*104*/ "Marimba ",
+/*105*/ "Koto ",
+/*106*/ "Sho ",
+/*107*/ "Shakuhachi",
+/*108*/ "Whistle 1 ",
+/*109*/ "Whistle 2 ",
+/*110*/ "BottleBlow",
+/*111*/ "BreathPipe",
+/*112*/ "Timpani ",
+/*113*/ "MelodicTom",
+/*114*/ "Deep Snare",
+/*115*/ "Elec Perc1",
+/*116*/ "Elec Perc2",
+/*117*/ "Taiko ",
+/*118*/ "Taiko Rim ",
+/*119*/ "Cymbal ",
+/*120*/ "Castanets ",
+/*121*/ "Triangle ",
+/*122*/ "Orche Hit ",
+/*123*/ "Telephone ",
+/*124*/ "Bird Tweet",
+/*125*/ "OneNoteJam",
+/*126*/ "WaterBells",
+/*127*/ "JungleTune" };
+
+char *MT32_RhythmTimbre[30] = {
+/*00*/ "Acou BD ",
+/*01*/ "Acou SD ",
+/*02*/ "Acou HiTom",
+/*03*/ "AcouMidTom",
+/*04*/ "AcouLowTom",
+/*05*/ "Elec SD ",
+/*06*/ "Clsd HiHat",
+/*07*/ "OpenHiHat1",
+/*08*/ "Crash Cym ",
+/*09*/ "Ride Cym ",
+/*10*/ "Rim Shot ",
+/*11*/ "Hand Clap ",
+/*12*/ "Cowbell ",
+/*13*/ "Mt HiConga",
+/*14*/ "High Conga",
+/*15*/ "Low Conga ",
+/*16*/ "Hi Timbale",
+/*17*/ "LowTimbale",
+/*18*/ "High Bongo",
+/*19*/ "Low Bongo ",
+/*20*/ "High Agogo",
+/*21*/ "Low Agogo ",
+/*22*/ "Tambourine",
+/*23*/ "Claves ",
+/*24*/ "Maracas ",
+/*25*/ "SmbaWhis L",
+/*26*/ "SmbaWhis S",
+/*27*/ "Cabasa ",
+/*28*/ "Quijada ",
+/*29*/ "OpenHiHat2" };
diff --git a/engines/sci/sfx/mixer.h b/engines/sci/sfx/mixer.h
new file mode 100644
index 0000000000..7cf2aae397
--- /dev/null
+++ b/engines/sci/sfx/mixer.h
@@ -0,0 +1,119 @@
+/***************************************************************************
+ sfx_mixer.h Copyright (C) 2003,04 Christoph Reichenbach
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+
+#ifndef _SFX_MIXER_H_
+#define _SFX_MIXER_H_
+
+#include <sfx_pcm.h>
+
+
+#define SFX_PCM_FEED_MODE_ALIVE 0
+#define SFX_PCM_FEED_MODE_DEAD 1
+
+struct twochannel_data {
+ int left, right;
+};
+
+typedef struct {
+ sfx_pcm_feed_t *feed;
+
+ /* The following fields are for use by the mixer only and must not be
+ ** touched by pcm_feed code. */
+ byte *buf; /* dynamically allocated buffer for this feed, used in some circumstances. */
+ int buf_size; /* Number of frames that fit into the buffer */
+ sfx_pcm_urat_t spd; /* source frames per destination frames */
+ sfx_pcm_urat_t scount; /* Frame counter, backed up in between calls */
+ int frame_bufstart; /* Left-over frames at the beginning of the buffer */
+ int mode; /* Whether the feed is alive or pending destruction */
+
+ int pending_review; /* Timestamp needs to be checked for this stream */
+ struct twochannel_data ch_old, ch_new; /* Intermediate results of output computation */
+} sfx_pcm_feed_state_t;
+
+
+typedef struct _sfx_pcm_mixer {
+ /* Mixers are the heart of all matters PCM. They take PCM data from subscribed feeds,
+ ** mix it (hence the name) and ask the pcm device they are attached to to play the
+ ** result. */
+
+ const char *name;
+ const char *version;
+
+ int (*init)(struct _sfx_pcm_mixer *self, sfx_pcm_device_t *device);
+ /* Initialises the mixer
+ ** Parameters: (sfx_pcm_mixer_t *) self: Self reference
+ ** (sfx_pcm_device_t *) device: An _already initialised_ PCM output driver
+ ** Returns : (int) SFX_OK on success, SFX_ERROR otherwise
+ */
+
+ void (*exit)(struct _sfx_pcm_mixer *self);
+ /* Uninitialises the mixer
+ ** Parameters: (sfx_pcm_mixer_t *) self: Self reference
+ ** Also uninitialises all feeds and the attached output device.
+ */
+
+ void (*subscribe)(struct _sfx_pcm_mixer *self, sfx_pcm_feed_t *feed);
+ /* Subscribes the mixer to a new feed
+ ** Parameters: (sfx_pcm_mixer_t *) self: Self reference
+ ** (sfx_pcm_feed_t *) feed: The feed to subscribe to
+ */
+
+ void (*pause)(struct _sfx_pcm_mixer *self);
+ /* Pauses the processing of input and output
+ */
+
+ void (*resume)(struct _sfx_pcm_mixer *self);
+ /* Resumes the processing of input and output after a pause
+ */
+
+ int (*process)(struct _sfx_pcm_mixer *self);
+ /* Processes all feeds, mixes their results, and passes everything to the output device
+ ** Returns : (int) SFX_OK on success, SFX_ERROR otherwise (output device error or
+ ** internal assertion failure)
+ ** Effects : All feeds are poll()ed, and the device is asked to output(). Buffer size
+ ** depends on the time that has passed since the last call to process(), if
+ ** any.
+ */
+
+ int feeds_nr;
+ int feeds_allocd;
+ sfx_pcm_feed_state_t *feeds;
+ sfx_pcm_device_t *dev;
+
+ void *private_bits;
+} sfx_pcm_mixer_t;
+
+sfx_pcm_mixer_t *
+sfx_pcm_find_mixer(char *name);
+/* Looks up a mixer by name, or a default mixer
+** Parameters: (char *) name: Name of the mixer to look for, or NULL to
+** take a default
+*/
+
+extern sfx_pcm_mixer_t *mixer; /* _THE_ global pcm mixer */
+
+#endif /* !defined(_SFX_MIXER_H_) */
diff --git a/engines/sci/sfx/mixer/Makefile.am b/engines/sci/sfx/mixer/Makefile.am
new file mode 100644
index 0000000000..684d40a4ba
--- /dev/null
+++ b/engines/sci/sfx/mixer/Makefile.am
@@ -0,0 +1,6 @@
+EXTRA_DIST = dc.c
+noinst_LIBRARIES = libscimixer.a
+INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@
+libscimixer_a_SOURCES = mixers.c soft.c
+test_LDADD = libscimixer.a ../../scicore/libscicore.a
+check_PROGRAMS = test
diff --git a/engines/sci/sfx/mixer/dc.c b/engines/sci/sfx/mixer/dc.c
new file mode 100644
index 0000000000..52b3ab5cbb
--- /dev/null
+++ b/engines/sci/sfx/mixer/dc.c
@@ -0,0 +1,329 @@
+/***************************************************************************
+ dc.c Copyright (C) 2005 Walter van Niftrik
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Walter van Niftrik <w.f.b.w.v.niftrik@stud.tue.nl>
+
+***************************************************************************/
+
+#include "../mixer.h"
+#include <sci_memory.h>
+#include <dc/sound/sound.h>
+#include <stream.h>
+#include <sys/queue.h>
+
+#define FEED_MODE_ALIVE 0
+#define FEED_MODE_IDLE 1
+#define FEED_MODE_DIEING 2
+#define FEED_MODE_DEAD 3
+#define FEED_MODE_RESTART 4
+
+typedef struct feed_state {
+ /* Queue entry. */
+ TAILQ_ENTRY(feed_state) entry;
+
+ /* Whether feed is alive or dead. */
+ int mode;
+
+ /* Blank gap in frames. */
+ int gap;
+
+ /* Stream handle. */
+ snd_stream_hnd_t handle;
+
+ /* Feed. */
+ sfx_pcm_feed_t *feed;
+
+ /* Timestamp of next frame requested by stream driver. */
+ sfx_timestamp_t time;
+} feed_state_t;
+
+TAILQ_HEAD(feed_list, feed_state) feeds;
+
+/* Buffer size in samples. */
+#define BUF_SIZE 0x4000
+
+static char buf[BUF_SIZE * 2];
+
+static feed_state_t *
+find_feed_state(snd_stream_hnd_t hnd)
+{
+ feed_state_t *state;
+ TAILQ_FOREACH(state, &feeds, entry) {
+ if (state->handle == hnd)
+ return state;
+ }
+
+ return NULL;
+}
+
+static void
+query_timestamp(feed_state_t *state)
+{
+ sfx_pcm_feed_t *feed = state->feed;
+
+ if (feed->get_timestamp) {
+ sfx_timestamp_t stamp;
+ int val = feed->get_timestamp(feed, &stamp);
+
+ switch (val) {
+ case PCM_FEED_TIMESTAMP:
+ state->gap = sfx_timestamp_frame_diff(stamp, state->time);
+
+ if (state->gap >= 0)
+ state->mode = FEED_MODE_ALIVE;
+ else {
+ long secs, usecs;
+
+ state->mode = FEED_MODE_RESTART;
+ sci_gettime(&secs, &usecs);
+ state->time = sfx_new_timestamp(secs, usecs, feed->conf.rate);
+ state->gap = sfx_timestamp_frame_diff(stamp, state->time);
+
+ if (state->gap < 0)
+ state->gap = 0;
+ }
+ break;
+ case PCM_FEED_IDLE:
+ state->mode = FEED_MODE_IDLE;
+ break;
+ case PCM_FEED_EMPTY:
+ state->mode = FEED_MODE_DIEING;
+ state->gap = BUF_SIZE;
+ }
+ } else {
+ state->mode = FEED_MODE_DIEING;
+ state->gap = BUF_SIZE;
+ }
+}
+
+void
+U8_to_S16(char *buf, int frames, int stereo)
+{
+ int samples = frames * (stereo ? 2 : 1);
+ int i;
+
+ for (i = samples - 1; i >= 0; i--) {
+ buf[i * 2 + 1] = (unsigned char) buf[i] - 128;
+ buf[i * 2] = 0;
+ }
+}
+
+static void *
+callback(snd_stream_hnd_t hnd, sfx_timestamp_t timestamp, int bytes_req, int *bytes_recv)
+{
+ feed_state_t *state = find_feed_state(hnd);
+ sfx_pcm_feed_t *feed;
+ int channels, frames_req;
+ int frames_recv = 0;
+
+ assert(state);
+
+ state->time = timestamp;
+ feed = state->feed;
+ channels = feed->conf.stereo == SFX_PCM_MONO ? 1 : 2;
+ frames_req = bytes_req / 2 / channels;
+
+ while (frames_req != frames_recv) {
+ int frames_left = frames_req - frames_recv;
+ char *buf_pos = buf + frames_recv * channels * 2;
+
+ if (state->mode == FEED_MODE_IDLE)
+ query_timestamp(state);
+
+ if (state->mode == FEED_MODE_IDLE) {
+ memset(buf_pos, 0, frames_left * channels * 2);
+
+ state->time = sfx_timestamp_add(state->time, frames_left);
+ break;
+ }
+
+ if (state->gap) {
+ int frames = state->gap;
+
+ if (frames > frames_left)
+ frames = frames_left;
+
+ memset(buf_pos, 0, frames * channels * 2);
+
+ state->gap -= frames;
+ frames_recv += frames;
+ state->time = sfx_timestamp_add(state->time, frames);
+ if (!state->gap && state->mode == FEED_MODE_DIEING) {
+ state->mode = FEED_MODE_DEAD;
+ break;
+ }
+ } else {
+ int frames = feed->poll(feed, buf_pos, frames_left);
+
+ if (feed->conf.format == SFX_PCM_FORMAT_U8)
+ U8_to_S16(buf_pos, frames, feed->conf.stereo != SFX_PCM_MONO);
+
+ frames_recv += frames;
+ state->time = sfx_timestamp_add(state->time, frames);
+
+ if (frames < frames_left)
+ query_timestamp(state);
+ }
+ }
+
+ *bytes_recv = bytes_req;
+ return buf;
+}
+
+static int
+mix_init(sfx_pcm_mixer_t *self, sfx_pcm_device_t *device)
+{
+ if (snd_stream_init() < 0) {
+ fprintf(stderr, "[dc-mixer] Failed to initialize streaming sound driver\n");
+ return SFX_ERROR;
+ }
+
+ TAILQ_INIT(&feeds);
+
+ return SFX_OK;
+}
+
+static void
+mix_subscribe(sfx_pcm_mixer_t *self, sfx_pcm_feed_t *feed)
+{
+ feed_state_t *state = sci_malloc(sizeof(feed_state_t));
+ long secs, usecs;
+
+ if ((feed->conf.format != SFX_PCM_FORMAT_S16_LE) &&
+ (feed->conf.format != SFX_PCM_FORMAT_U8)) {
+ fprintf(stderr, "[dc-mixer] Unsupported feed format\n");
+ feed->destroy(feed);
+ return;
+ }
+
+ state->handle = snd_stream_alloc(callback, BUF_SIZE);
+
+ if (state->handle == SND_STREAM_INVALID) {
+ fprintf(stderr, "[dc-mixer] Failed to allocate stream handle\n");
+ feed->destroy(feed);
+ return;
+ }
+
+ feed->frame_size = SFX_PCM_FRAME_SIZE(feed->conf);
+ state->mode = FEED_MODE_ALIVE;
+ state->feed = feed;
+ state->gap = 0;
+
+ TAILQ_INSERT_TAIL(&feeds, state, entry);
+
+ sci_gettime(&secs, &usecs);
+ state->time = sfx_new_timestamp(secs, usecs, feed->conf.rate);
+ snd_stream_start(state->handle, feed->conf.rate,
+ feed->conf.stereo != SFX_PCM_MONO);
+}
+
+static void
+mix_exit(sfx_pcm_mixer_t *self)
+{
+ snd_stream_shutdown();
+}
+
+static int
+mix_process(sfx_pcm_mixer_t *self)
+{
+ feed_state_t *state, *state_next;
+
+ TAILQ_FOREACH(state, &feeds, entry) {
+ snd_stream_poll(state->handle);
+ }
+
+ state = TAILQ_FIRST(&feeds);
+ while (state) {
+ state_next = TAILQ_NEXT(state, entry);
+ if (state->mode == FEED_MODE_DEAD) {
+ snd_stream_stop(state->handle);
+ snd_stream_destroy(state->handle);
+ state->feed->destroy(state->feed);
+ TAILQ_REMOVE(&feeds, state, entry);
+ }
+ else if (state->mode == FEED_MODE_RESTART) {
+ snd_stream_stop(state->handle);
+ snd_stream_start(state->handle, state->feed->conf.rate,
+ state->feed->conf.stereo != SFX_PCM_MONO);
+ state->mode = FEED_MODE_ALIVE;
+ }
+ state = state_next;
+ }
+
+ return SFX_OK;
+}
+
+static void
+mix_pause(sfx_pcm_mixer_t *self)
+{
+}
+
+static void
+mix_resume(sfx_pcm_mixer_t *self)
+{
+}
+
+static int
+pcm_init(sfx_pcm_device_t *self)
+{
+ return SFX_OK;
+}
+
+static void
+pcm_exit(sfx_pcm_device_t *self)
+{
+}
+
+sfx_pcm_device_t sfx_pcm_driver_dc = {
+ "dc",
+ "0.1",
+
+ pcm_init,
+ pcm_exit,
+ NULL,
+ NULL,
+ NULL,
+
+ {0, 0, 0},
+ 0,
+ NULL,
+ NULL
+};
+
+sfx_pcm_mixer_t sfx_pcm_mixer_dc = {
+ "dc",
+ "0.1",
+
+ mix_init,
+ mix_exit,
+ mix_subscribe,
+ mix_pause,
+ mix_resume,
+ mix_process,
+
+ 0,
+ 0,
+ NULL,
+ NULL,
+ NULL
+};
diff --git a/engines/sci/sfx/mixer/mixers.c b/engines/sci/sfx/mixer/mixers.c
new file mode 100644
index 0000000000..1c7e27ab17
--- /dev/null
+++ b/engines/sci/sfx/mixer/mixers.c
@@ -0,0 +1,55 @@
+/***************************************************************************
+ mixers.c Copyright (C) 2003 Christoph Reichenbach
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+
+#include "../mixer.h"
+#include <resource.h>
+
+extern sfx_pcm_mixer_t sfx_pcm_mixer_soft_linear;
+
+#ifdef _DREAMCAST
+extern sfx_pcm_mixer_t sfx_pcm_mixer_dc;
+#endif
+
+static sfx_pcm_mixer_t *mixers[] = {
+#ifdef _DREAMCAST
+ &sfx_pcm_mixer_dc,
+#endif
+ &sfx_pcm_mixer_soft_linear,
+ NULL
+};
+
+sfx_pcm_mixer_t *
+sfx_pcm_find_mixer(char *name)
+{
+ int i = 0;
+
+ if (name)
+ while (mixers[i] && strcmp(name, mixers[i]->name))
+ ++i;
+
+ return mixers[i];
+}
diff --git a/engines/sci/sfx/mixer/soft.c b/engines/sci/sfx/mixer/soft.c
new file mode 100644
index 0000000000..9f833017df
--- /dev/null
+++ b/engines/sci/sfx/mixer/soft.c
@@ -0,0 +1,988 @@
+/***************************************************************************
+ mixer.c Copyright (C) 2003 Christoph Reichenbach
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+
+#include "../mixer.h"
+#include <sci_memory.h>
+
+/* Max. number of microseconds in difference allowed between independent audio streams */
+#define TIMESTAMP_MAX_ALLOWED_DELTA 2000
+
+/*#define DEBUG 3*/
+/* Set DEBUG to one of the following:
+** anything -- high-level debugging (feed subscriptions/deletions etc.)
+** >= 1 -- rough input and output analysis (once per call)
+** >= 2 -- more detailed input analysis (once per call and feed)
+** >= 3 -- fully detailed input and output analysis (once per frame and feed)
+*/
+
+/*#define DEBUG 0*/
+
+#define MIN_DELTA_OBSERVATIONS 100 /* Number of times the mixer is called before it starts trying to improve latency */
+#define MAX_DELTA_OBSERVATIONS 1000000 /* Number of times the mixer is called before we assume we truly understand timing */
+
+static int diagnosed_too_slow = 0;
+
+static volatile int mixer_lock = 0;
+
+/*#define DEBUG_LOCKS*/
+#ifdef DEBUG_LOCKS
+# define DEBUG_ACQUIRE fprintf(stderr, "[ -LOCK -] ACKQ %d: %d\n", __LINE__, mixer_lock)
+# define DEBUG_WAIT fprintf(stderr, "[ -LOCK -] WAIT %d: %d\n", __LINE__, mixer_lock);
+# define DEBUG_RELEASE ; fprintf(stderr, "[ -LOCK -] REL %d: %d\n", __LINE__, mixer_lock);
+#else
+# define DEBUG_ACQUIRE
+# define DEBUG_WAIT
+# define DEBUG_RELEASE
+#endif
+
+#define ACQUIRE_LOCK() ++mixer_lock; while (mixer_lock != 1) { DEBUG_WAIT sci_sched_yield(); } DEBUG_ACQUIRE
+#define RELEASE_LOCK() --mixer_lock DEBUG_RELEASE
+
+struct mixer_private {
+ byte *outbuf; /* Output buffer to write to the PCM device next time */
+ sfx_timestamp_t outbuf_timestamp; /* Timestamp associated with the output buffer */
+ int have_outbuf_timestamp; /* Whether we really _have_ an associated timestamp */
+ byte *writebuf; /* Buffer we're supposed to write to */
+ gint32 *compbuf_l, *compbuf_r; /* Intermediate buffers for computation */
+ int lastbuf_len; /* Number of frames stored in the last buffer */
+
+ long skew; /* Millisecond relative to which we compute time. This is the millisecond
+ ** part of the first time we emitted sound, to simplify some computations. */
+ long lsec; /* Last point in time we updated buffers, if any (seconds since the epoch) */
+ int played_this_second; /* Number of frames emitted so far in second lsec */
+
+ int max_delta; /* maximum observed time delta (using 'frames' as a metric unit) */
+ int delta_observations; /* Number of times we played; confidence measure for max_delta */
+
+ /* Pause data */
+ int paused;
+};
+
+#define P ((struct mixer_private *)(self->private_bits))
+
+
+static int
+mix_init(sfx_pcm_mixer_t *self, sfx_pcm_device_t *device)
+{
+ self->dev = device;
+ self->private_bits /* = P */ = sci_malloc(sizeof(struct mixer_private));
+ P->outbuf = P->writebuf = NULL;
+ P->lastbuf_len = 0;
+ P->compbuf_l = (gint32*)sci_malloc(sizeof(gint32) * device->buf_size);
+ P->compbuf_r = (gint32*)sci_malloc(sizeof(gint32) * device->buf_size);
+ P->played_this_second = 0;
+ P->paused = 0;
+#ifdef DEBUG
+ sciprintf("[soft-mixer] Initialised device %s v%s (%d Hz, %d/%x)\n",
+ device->name, device->version,
+ device->conf.rate, device->conf.stereo, device->conf.format);
+#endif
+ return SFX_OK;
+}
+
+static inline unsigned int
+gcd(unsigned int a, unsigned int b)
+{
+ if (a == b)
+ return a;
+
+ if (a < b) {
+ unsigned int c = b % a;
+
+ if (!c)
+ return a;
+
+ return gcd(c, a);
+ } else
+ return gcd(b, a);
+}
+
+static sfx_pcm_urat_t
+urat(unsigned int nom, unsigned int denom)
+{
+ sfx_pcm_urat_t rv;
+ unsigned int g;
+
+ rv.val = nom / denom;
+ nom -= rv.val * denom;
+ if (nom == 0)
+ g = 1;
+ else
+ g = gcd(nom, denom);
+
+ rv.nom = nom / g;
+ rv.den = denom / g;
+
+ return rv;
+}
+
+static void
+mix_subscribe(sfx_pcm_mixer_t *self, sfx_pcm_feed_t *feed)
+{
+ sfx_pcm_feed_state_t *fs;
+ ACQUIRE_LOCK();
+ if (!self->feeds) {
+ self->feeds_allocd = 2;
+ self->feeds = (sfx_pcm_feed_state_t*)sci_malloc(sizeof(sfx_pcm_feed_state_t)
+ * self->feeds_allocd);
+ } else if (self->feeds_allocd == self->feeds_nr) {
+ self->feeds_allocd += 2;
+ self->feeds = (sfx_pcm_feed_state_t*)sci_realloc(self->feeds,
+ sizeof(sfx_pcm_feed_state_t)
+ * self->feeds_allocd);
+ }
+
+ fs = self->feeds + self->feeds_nr++;
+ fs->feed = feed;
+
+ feed->frame_size = SFX_PCM_FRAME_SIZE(feed->conf);
+
+ /* fs->buf_size = (self->dev->buf_size
+ * (feed->conf.rate
+ + self->dev->conf.rate - 1))
+ / self->dev->conf.rate;
+ */
+ /* For the sake of people without 64 bit CPUs: */
+ fs->buf_size = 2 + /* Additional safety */
+ (self->dev->buf_size *
+ (1 + (feed->conf.rate / self->dev->conf.rate)));
+fprintf(stderr, " ---> %d/%d/%d/%d = %d\n",
+ self->dev->buf_size,
+ feed->conf.rate,
+ self->dev->conf.rate,
+ feed->frame_size,
+ fs->buf_size);
+
+ fs->buf = (byte*)sci_malloc(fs->buf_size * feed->frame_size);
+fprintf(stderr, " ---> --> %d for %p at %p\n", fs->buf_size * feed->frame_size, (void *)fs, (void *)fs->buf);
+{int i; for (i = 0; i < fs->buf_size * feed->frame_size; i++)
+fs->buf[i] = 0xa5; }
+ fs->scount = urat(0, 1);
+ fs->spd = urat(feed->conf.rate, self->dev->conf.rate);
+ fs->scount.den = fs->spd.den;
+ fs->ch_old.left = 0;
+ fs->ch_old.right = 0;
+ fs->ch_new.left = 0;
+ fs->ch_new.right = 0;
+ fs->mode = SFX_PCM_FEED_MODE_ALIVE;
+
+ /* If the feed can't provide us with timestamps, we don't need to wait for it to do so */
+ fs->pending_review = (feed->get_timestamp)? 1 : 0;
+
+ fs->frame_bufstart = 0;
+
+#ifdef DEBUG
+ sciprintf("[soft-mixer] Subscribed %s-%x (%d Hz, %d/%x) at %d+%d/%d, buffer size %d\n",
+ feed->debug_name, feed->debug_nr, feed->conf.rate, feed->conf.stereo, feed->conf.format,
+ fs->spd.val, fs->spd.nom, fs->spd.den, fs->buf_size);
+#endif
+ RELEASE_LOCK();
+}
+
+
+static void
+_mix_unsubscribe(sfx_pcm_mixer_t *self, sfx_pcm_feed_t *feed)
+{
+ int i;
+#ifdef DEBUG
+ sciprintf("[soft-mixer] Unsubscribing %s-%x\n", feed->debug_name, feed->debug_nr);
+#endif
+ for (i = 0; i < self->feeds_nr; i++) {
+ sfx_pcm_feed_state_t *fs = self->feeds + i;
+
+ if (fs->feed == feed) {
+ feed->destroy(feed);
+
+ if (fs->buf)
+ sci_free(fs->buf);
+
+ self->feeds_nr--;
+
+ /* Copy topmost into deleted so that we don't have any holes */
+ if (i != self->feeds_nr)
+ self->feeds[i] = self->feeds[self->feeds_nr];
+
+ if (self->feeds_allocd > 8 && self->feeds_allocd > (self->feeds_nr << 1)) {
+ /* Limit memory waste */
+ self->feeds_allocd >>= 1;
+ self->feeds
+ = (sfx_pcm_feed_state_t*)sci_realloc(self->feeds,
+ sizeof(sfx_pcm_feed_state_t)
+ * self->feeds_allocd);
+ }
+
+ for (i = 0; i < self->feeds_nr; i++)
+ fprintf(stderr, " Feed #%d: %s-%x\n",
+ i, self->feeds[i].feed->debug_name,
+ self->feeds[i].feed->debug_nr);
+
+ return;
+ }
+ }
+
+ fprintf(stderr, "[sfx-mixer] Assertion failed: Deleting invalid feed %p out of %d\n",
+ (void *)feed, self->feeds_nr);
+
+ BREAKPOINT();
+}
+
+static void
+mix_unsubscribe(sfx_pcm_mixer_t *self, sfx_pcm_feed_t *feed)
+{
+ ACQUIRE_LOCK();
+ _mix_unsubscribe(self, feed);
+ RELEASE_LOCK();
+}
+
+
+static void
+mix_exit(sfx_pcm_mixer_t *self)
+{
+ ACQUIRE_LOCK();
+ while (self->feeds_nr)
+ _mix_unsubscribe(self, self->feeds[0].feed);
+
+ if (P->outbuf)
+ sci_free(P->outbuf);
+ if (P->writebuf)
+ sci_free(P->writebuf);
+
+ if (P->compbuf_l)
+ sci_free(P->compbuf_l);
+ if (P->compbuf_l)
+ sci_free(P->compbuf_r);
+
+ sci_free(P);
+ self->private_bits /* = P */ = NULL;
+ RELEASE_LOCK();
+
+#ifdef DEBUG
+ sciprintf("[soft-mixer] Uninitialising mixer\n");
+#endif
+}
+
+
+#define LIMIT_16_BITS(v) \
+ if (v < -32767) \
+ v = -32768; \
+ else if (v > 32766) \
+ v = 32767
+
+static inline void
+mix_compute_output(sfx_pcm_mixer_t *self, int outplen)
+{
+ int frame_i;
+ sfx_pcm_config_t conf = self->dev->conf;
+ int use_16 = conf.format & SFX_PCM_FORMAT_16;
+ int bias = conf.format & ~SFX_PCM_FORMAT_LMASK;
+ byte *lchan, *rchan = NULL;
+ /* Don't see how this could possibly wind up being
+ ** used w/o initialisation, but you never know... */
+ gint32 *lsrc = P->compbuf_l;
+ gint32 *rsrc = P->compbuf_r;
+ int frame_size = SFX_PCM_FRAME_SIZE(conf);
+
+
+ if (!P->writebuf)
+ P->writebuf = (byte*)sci_malloc(self->dev->buf_size * frame_size + 4);
+
+ if (conf.stereo) {
+ if (conf.stereo == SFX_PCM_STEREO_RL) {
+ lchan = P->writebuf + ((use_16)? 2 : 1);
+ rchan = P->writebuf;
+ } else {
+ lchan = P->writebuf;
+ rchan = P->writebuf + ((use_16)? 2 : 1);
+ }
+ } else
+ lchan = P->writebuf;
+
+
+ for (frame_i = 0; frame_i < outplen; frame_i++) {
+ int left = *lsrc++;
+ int right = *rsrc++;
+
+ if (conf.stereo) {
+ LIMIT_16_BITS(left);
+ LIMIT_16_BITS(right);
+
+ if (!use_16) {
+ left >>= 8;
+ right >>= 8;
+ }
+
+ left += bias;
+ right += bias;
+
+ if (use_16) {
+ if (SFX_PCM_FORMAT_LE == (conf.format & SFX_PCM_FORMAT_ENDIANNESS)) {
+ lchan[0] = left & 0xff;
+ lchan[1] = (left >> 8) & 0xff;
+ rchan[0] = right & 0xff;
+ rchan[1] = (right >> 8) & 0xff;
+ } else {
+ lchan[1] = left & 0xff;
+ lchan[0] = (left >> 8) & 0xff;
+ rchan[1] = right & 0xff;
+ rchan[0] = (right >> 8) & 0xff;
+ }
+
+ lchan += 4;
+ rchan += 4;
+ } else {
+ *lchan = left & 0xff;
+ *rchan = right & 0xff;
+
+ lchan += 2;
+ rchan += 2;
+ }
+
+ } else {
+ left += right;
+ left >>= 1;
+ LIMIT_16_BITS(left);
+ if (!use_16)
+ left >>= 8;
+
+ left += bias;
+
+ if (use_16) {
+ if (SFX_PCM_FORMAT_LE == (conf.format & SFX_PCM_FORMAT_ENDIANNESS)) {
+ lchan[0] = left & 0xff;
+ lchan[1] = (left >> 8) & 0xff;
+ } else {
+ lchan[1] = left & 0xff;
+ lchan[0] = (left >> 8) & 0xff;
+ }
+
+ lchan += 2;
+ } else {
+ *lchan = left & 0xff;
+ lchan += 1;
+ }
+ }
+ }
+}
+
+static inline void
+mix_swap_buffers(sfx_pcm_mixer_t *self)
+{ /* Swap buffers */
+ byte *tmp = P->outbuf;
+ P->outbuf = P->writebuf;
+ P->writebuf = tmp;
+}
+
+
+#define FRAME_OFFSET(usecs) \
+ ((usecs >> 7) /* approximate, since uint32 is too small */ \
+ * ((long) self->dev->conf.rate)) \
+ / (1000000L >> 7)
+
+static inline int
+mix_compute_buf_len(sfx_pcm_mixer_t *self, int *skip_frames)
+ /* Computes the number of frames we ought to write. It tries to minimise the number,
+ ** in order to reduce latency. */
+ /* It sets 'skip_frames' to the number of frames to assume lost by latency, effectively
+ ** skipping them. */
+{
+ int free_frames;
+ int played_frames = 0; /* since the last call */
+ long secs, usecs;
+ int frame_pos;
+ int result_frames;
+
+ sci_gettime(&secs, &usecs);
+
+ if (!P->outbuf) {
+ /* Called for the first time ever? */
+ P->skew = usecs;
+ P->lsec = secs;
+ P->max_delta = 0;
+ P->delta_observations = 0;
+ P->played_this_second = 0;
+ *skip_frames = 0;
+ return self->dev->buf_size;
+ }
+
+ /* fprintf(stderr, "[%d:%d]S%d ", secs, usecs, P->skew);*/
+
+ if (P->skew > usecs) {
+ secs--;
+ usecs += (1000000 - P->skew);
+ }
+ else
+ usecs -= P->skew;
+
+ frame_pos = FRAME_OFFSET(usecs);
+
+ played_frames = frame_pos - P->played_this_second
+ + ((secs - P->lsec) * self->dev->conf.rate);
+ /*
+ fprintf(stderr, "%d:%d - %d:%d => %d\n", secs, frame_pos,
+ P->lsec, P->played_this_second, played_frames);
+ */
+
+ if (played_frames > self->dev->buf_size)
+ played_frames = self->dev->buf_size;
+
+ /*
+ fprintf(stderr, "Between %d:? offset=%d and %d:%d offset=%d: Played %d at %d\n", P->lsec, P->played_this_second,
+ secs, usecs, frame_pos, played_frames, self->dev->conf.rate);
+ */
+
+
+ if (played_frames > P->max_delta)
+ P->max_delta = played_frames;
+
+ free_frames = played_frames;
+
+ if (free_frames > self->dev->buf_size) {
+ if (!diagnosed_too_slow) {
+ sciprintf("[sfx-mixer] Your timer is too slow for your PCM output device (%d/%d), free=%d.\n"
+ "[sfx-mixer] You might want to try changing the device, timer, or mixer, if possible.\n",
+ played_frames, self->dev->buf_size, free_frames);
+ }
+ diagnosed_too_slow = 1;
+
+ *skip_frames = free_frames - self->dev->buf_size;
+ free_frames = self->dev->buf_size;
+ } else
+ *skip_frames = 0;
+
+ ++P->delta_observations;
+ if (P->delta_observations > MAX_DELTA_OBSERVATIONS)
+ P->delta_observations = MAX_DELTA_OBSERVATIONS;
+
+/* /\* Disabled, broken *\/ */
+/* if (0 && P->delta_observations > MIN_DELTA_OBSERVATIONS) { /\* Start improving after a while *\/ */
+/* int diff = self->dev->conf.rate - P->max_delta; */
+
+/* /\* log-approximate P->max_delta over time *\/ */
+/* recommended_frames = P->max_delta + */
+/* ((diff * MIN_DELTA_OBSERVATIONS) / P->delta_observations); */
+/* /\* WTF? *\/ */
+/* } else */
+/* recommended_frames = self->dev->buf_size; /\* Initially, keep the buffer full *\/ */
+
+#if (DEBUG >= 1)
+ sciprintf("[soft-mixer] played since last time: %d, recommended: %d, free: %d\n",
+ played_frames, recommended_frames, free_frames);
+#endif
+
+ result_frames = free_frames;
+
+ if (result_frames < 0)
+ result_frames = 0;
+
+ P->played_this_second += result_frames;
+ while (P->played_this_second >= self->dev->conf.rate) {
+ /* Won't normally happen more than once */
+ P->played_this_second -= self->dev->conf.rate;
+ P->lsec++;
+ }
+
+ if (result_frames > self->dev->buf_size) {
+ fprintf(stderr, "[soft-mixer] Internal assertion failed: frames-to-write %d > %d\n",
+ result_frames, self->dev->buf_size);
+ }
+ return result_frames;
+}
+
+
+
+#define READ_NEW_VALUES() \
+ if (frames_left > 0) { \
+ if (bias) { /* unsigned data */ \
+ if (!use_16) { \
+ c_new.left = (*lsrc) << 8; \
+ c_new.right = (*rsrc) << 8; \
+ } else { \
+ if (conf.format & SFX_PCM_FORMAT_LE) { \
+ c_new.left = lsrc[0] | lsrc[1] << 8; \
+ c_new.right = rsrc[0] | rsrc[1] << 8; \
+ } else { \
+ c_new.left = lsrc[1] | lsrc[0] << 8; \
+ c_new.right = rsrc[1] | rsrc[0] << 8; \
+ } \
+ } \
+ } else { /* signed data */ \
+ if (!use_16) { \
+ c_new.left = (*((signed char *)lsrc)) << 8; \
+ c_new.right = (*((signed char *)rsrc)) << 8; \
+ } else { \
+ if (conf.format & SFX_PCM_FORMAT_LE) { \
+ c_new.left = lsrc[0] | ((signed char *)lsrc)[1] << 8; \
+ c_new.right = rsrc[0] | ((signed char *)rsrc)[1] << 8; \
+ } else { \
+ c_new.left = lsrc[1] | ((signed char *)lsrc)[0] << 8; \
+ c_new.right = rsrc[1] | ((signed char *)rsrc)[0] << 8; \
+ } \
+ } \
+ } \
+ \
+ c_new.left -= bias; \
+ c_new.right -= bias; \
+ \
+ lsrc += frame_size; \
+ rsrc += frame_size; \
+ } else { \
+ c_new.left = c_new.right = 0; \
+ break; \
+ }
+
+
+static volatile int xx_offset;
+static volatile int xx_size;
+
+static void
+mix_compute_input_linear(sfx_pcm_mixer_t *self, int add_result,
+ int len, sfx_timestamp_t *ts, sfx_timestamp_t base_ts)
+ /* if add_result is non-zero, P->outbuf should be added to rather than overwritten. */
+ /* base_ts is the timestamp for the first frame */
+{
+ sfx_pcm_feed_state_t *fs = self->feeds + add_result;
+ sfx_pcm_feed_t *f = fs->feed;
+ sfx_pcm_config_t conf = f->conf;
+ int use_16 = conf.format & SFX_PCM_FORMAT_16;
+ gint32 *lchan = P->compbuf_l;
+ gint32 *rchan = P->compbuf_r;
+ int frame_size = f->frame_size;
+ byte *wr_dest = fs->buf + (frame_size * fs->frame_bufstart);
+ byte *lsrc = fs->buf;
+ byte *rsrc = fs->buf;
+ /* Location to write to */
+ int frames_nr;
+ int bias = (conf.format & ~SFX_PCM_FORMAT_LMASK)? 0x8000 : 0;
+ /* We use this only on a 16 bit level here */
+
+ /* The two most extreme source frames we consider for a
+ ** destination frame */
+ struct twochannel_data c_old = fs->ch_old;
+ struct twochannel_data c_new = fs->ch_new;
+
+ int frames_read = 0;
+ int frames_left;
+ int write_offset; /* Iterator for translation */
+ int delay_frames = 0; /* Number of frames (dest buffer) at the beginning we skip */
+
+ /* First, compute the number of frames we want to retreive */
+ frames_nr = fs->spd.val * len;
+ /* A little complicated since we must consider partial frames */
+ frames_nr += (fs->spd.nom * len
+ + (fs->scount.den - fs->scount.nom) /* remember that we may have leftovers */
+ + (fs->spd.den - 1 /* round up */)
+ )
+ / fs->spd.den;
+
+ ts->secs = -1;
+
+ if (frames_nr > fs->buf_size) {
+ fprintf(stderr, "%d (%d*%d + somethign) bytes, but only %d allowed!!!!!\n",
+ frames_nr * f->frame_size,
+ fs->spd.val, len,
+ fs->buf_size);
+ BREAKPOINT();
+ }
+
+ if (fs->pending_review) {
+ int newmode = PCM_FEED_EMPTY; /* empty unless a get_timestamp() tells otherwise */
+
+ RELEASE_LOCK();
+ /* Retrieve timestamp */
+ if (f->get_timestamp)
+ newmode = f->get_timestamp(f, ts);
+ ACQUIRE_LOCK();
+
+ fs = self->feeds + add_result;
+ /* Reset in case of status update */
+
+ switch (newmode) {
+
+ case PCM_FEED_TIMESTAMP: {
+ /* Compute the number of frames the returned timestamp is in the future: */
+ delay_frames =
+ sfx_timestamp_frame_diff(sfx_timestamp_renormalise(*ts, base_ts.frame_rate),
+ base_ts);
+
+ if (delay_frames <= 0)
+ /* Start ASAP, even if it's too late */
+ delay_frames = 0;
+ else
+ if (delay_frames > len)
+ delay_frames = len;
+ fs->pending_review = 0;
+ }
+ break;
+
+ case PCM_FEED_EMPTY:
+ fs->mode = SFX_PCM_FEED_MODE_DEAD;
+
+ /* ...fall through... */
+
+ case PCM_FEED_IDLE:
+ /* Clear audio buffer, if neccessary, and return */
+ if (!add_result) {
+ memset(P->compbuf_l, 0, sizeof(gint32) * len);
+ memset(P->compbuf_r, 0, sizeof(gint32) * len);
+ }
+ return;
+
+ default:
+ fprintf(stderr, "[soft-mixer] Fatal: Invalid mode returned by PCM feed %s-%d's get_timestamp(): %d\n",
+ f->debug_name, f->debug_nr, newmode);
+ exit(1);
+ }
+ }
+
+ RELEASE_LOCK();
+ /* Make sure we have sufficient information */
+ if (frames_nr > delay_frames + fs->frame_bufstart)
+ frames_read =
+ f->poll(f, wr_dest,
+ frames_nr
+ - delay_frames
+ - fs->frame_bufstart);
+
+ ACQUIRE_LOCK();
+ fs = self->feeds + add_result;
+
+ frames_read += fs->frame_bufstart;
+ frames_left = frames_read;
+
+ /* Reset in case of status update */
+
+ /* Skip at the beginning: */
+ if (delay_frames) {
+ if (!add_result) {
+ memset(lchan, 0, sizeof(gint32) * delay_frames);
+ memset(rchan, 0, sizeof(gint32) * delay_frames);
+ }
+ lchan += delay_frames;
+ rchan += delay_frames;
+
+ len -= delay_frames;
+ }
+
+
+#if (DEBUG >= 2)
+ sciprintf("[soft-mixer] Examining %s-%x (frame size %d); read %d/%d/%d, re-using %d frames\n",
+ f->debug_name, f->debug_nr, frame_size, frames_read, frames_nr,
+ fs->buf_size, fs->frame_bufstart);
+#endif
+
+
+ if (conf.stereo == SFX_PCM_STEREO_LR)
+ rsrc += (use_16)? 2 : 1;
+ else if (conf.stereo == SFX_PCM_STEREO_RL)
+ lsrc += (use_16)? 2 : 1;
+ /* Otherwise, we let both point to the same place */
+
+#if (DEBUG >= 2)
+ sciprintf("[soft-mixer] Stretching theoretical %d (physical %d) results to %d\n", frames_nr, frames_left, len);
+#endif
+ for (write_offset = 0; write_offset < len; write_offset++) {
+ int leftsum = 0; /* Sum of any complete frames we used */
+ int rightsum = 0;
+
+ int left; /* Sum of the two most extreme source frames
+ ** we considered, i.e. the oldest and newest
+ ** one corresponding to the output frame we are
+ ** computing */
+ int right;
+
+ int frame_steps = fs->spd.val;
+ int j;
+
+ if (fs->scount.nom >= fs->scount.den) {
+ fs->scount.nom -= fs->scount.den; /* Ensure fractional part < 1 */
+ ++frame_steps;
+ }
+ if (frame_steps)
+ c_old = c_new;
+
+#if 0
+ if (write_offset == 0) {
+ READ_NEW_VALUES();
+ --frames_left;
+#if (DEBUG >= 3)
+ sciprintf("[soft-mixer] Initial read %d:%d\n", c_new.left, c_new.right);
+#endif
+ c_old = c_new;
+ }
+#endif
+
+ for (j = 0; j < frame_steps; j++) {
+ READ_NEW_VALUES();
+ --frames_left;
+#if (DEBUG >= 3)
+ sciprintf("[soft-mixer] Step %d/%d made %d:%d\n", j, frame_steps, c_new.left, c_new.right);
+#endif
+
+ /* The last frame will be subject to the fractional
+ ** part analysis, so we add it to 'left' and 'right'
+ ** later-- all others are added to (leftsum, rightsum).
+ */
+ if (j+1 < frame_steps) {
+ leftsum += c_new.left;
+ rightsum += c_new.right;
+ }
+ }
+
+ left = c_new.left * fs->scount.nom
+ + c_old.left * (fs->scount.den - fs->scount.nom);
+ right = c_new.right * fs->scount.nom
+ + c_old.right * (fs->scount.den - fs->scount.nom);
+
+ /* Normalise */
+ left /= fs->spd.den;
+ right /= fs->spd.den;
+
+
+ leftsum += left;
+ rightsum += right;
+
+
+ /* Make sure to divide by the number of frames we added here */
+ if (frame_steps > 1) {
+ leftsum /= (frame_steps);
+ rightsum /= (frame_steps);
+ }
+
+
+#if (DEBUG >= 3)
+ sciprintf("[soft-mixer] Ultimate result: %d:%d (frac %d:%d)\n", leftsum, rightsum, left, right);
+#endif
+
+ if (add_result) {
+ *(lchan++) += leftsum;
+ *(rchan++) += rightsum;
+ } else {
+ *(lchan++) = leftsum;
+ *(rchan++) = rightsum;
+ }
+
+ fs->scount.nom += fs->spd.nom; /* Count up fractional part */
+ }
+
+ fs->ch_old = c_old;
+ fs->ch_new = c_new;
+
+ /* If neccessary, zero out the rest */
+ if (write_offset < len && !add_result) {
+ memset(lchan, 0, sizeof(gint32) * (len - write_offset));
+ memset(rchan, 0, sizeof(gint32) * (len - write_offset));
+ }
+
+ /* Save whether we have a partial frame still stored */
+ fs->frame_bufstart = frames_left;
+
+ if (frames_left) {
+ xx_offset = ((frames_read - frames_left) * f->frame_size);
+ xx_size = frames_left * f->frame_size;
+ if (xx_offset + xx_size
+ >= fs->buf_size * f->frame_size) {
+ fprintf(stderr, "offset %d >= max %d!\n",
+ (xx_offset + xx_size), fs->buf_size * f->frame_size);
+ BREAKPOINT();
+ }
+
+ memmove(fs->buf,
+ fs->buf + ((frames_read - frames_left) * f->frame_size),
+ frames_left * f->frame_size);
+ }
+#if (DEBUG >= 2)
+ sciprintf("[soft-mixer] Leaving %d over\n", fs->frame_bufstart);
+#endif
+
+ if (frames_read + delay_frames < frames_nr) {
+ if (f->get_timestamp) /* Can resume? */
+ fs->pending_review = 1;
+ else
+ fs->mode = SFX_PCM_FEED_MODE_DEAD; /* Done. */
+ }
+}
+
+static int
+mix_process_linear(sfx_pcm_mixer_t *self)
+{
+ACQUIRE_LOCK();
+{
+ int src_i; /* source feed index counter */
+ int frames_skip; /* Number of frames to discard, rather than to emit */
+ int buflen = mix_compute_buf_len(self, &frames_skip); /* Compute # of frames we must compute and write */
+ int fake_buflen;
+ int timestamp_max_delta = 0;
+ int have_timestamp = 0;
+ sfx_timestamp_t start_timestamp; /* The timestamp at which the first frame will be played */
+ sfx_timestamp_t min_timestamp;
+ sfx_timestamp_t timestamp;
+
+ if (self->dev->get_output_timestamp)
+ start_timestamp = self->dev->get_output_timestamp(self->dev);
+ else {
+ long sec, usec;
+ sci_gettime(&sec, &usec);
+ start_timestamp = sfx_new_timestamp(sec, usec, self->dev->conf.rate);
+ }
+
+ if ((P->outbuf) && (P->lastbuf_len)) {
+ sfx_timestamp_t ts;
+ int rv;
+
+ if (P->have_outbuf_timestamp) {
+ ts = sfx_timestamp_renormalise(P->outbuf_timestamp, self->dev->conf.rate);
+ }
+
+ rv = self->dev->output(self->dev, P->outbuf,
+ P->lastbuf_len,
+ (P->have_outbuf_timestamp)? &ts : NULL);
+
+ if (rv == SFX_ERROR) {
+ RELEASE_LOCK();
+ return rv; /* error */
+ }
+ }
+
+#if (DEBUG >= 1)
+ if (self->feeds_nr)
+ sciprintf("[soft-mixer] Mixing %d output frames on %d input feeds\n", buflen, self->feeds_nr);
+#endif
+ if (self->feeds_nr && !P->paused) {
+ /* Below, we read out all feeds in case we have to skip frames first, then get the
+ ** most current sound. 'fake_buflen' is either the actual buflen (for the last iteration)
+ ** or a fraction of the buf length to discard. */
+ do {
+ if (frames_skip) {
+ if (frames_skip > self->dev->buf_size)
+ fake_buflen = self->dev->buf_size;
+ else
+ fake_buflen = frames_skip;
+
+ frames_skip -= fake_buflen;
+ } else {
+ fake_buflen = buflen;
+ frames_skip = -1; /* Mark us as being completely done */
+ }
+
+ for (src_i = 0; src_i < self->feeds_nr; src_i++) {
+ mix_compute_input_linear(self, src_i,
+ fake_buflen, &timestamp,
+ start_timestamp);
+
+ if (timestamp.secs >= 0) {
+ if (have_timestamp) {
+ int diff = sfx_timestamp_usecs_diff(min_timestamp, timestamp);
+ if (diff > 0) {
+ /* New earlier timestamp */
+ timestamp = min_timestamp;
+ timestamp_max_delta += diff;
+ } else if (diff > timestamp_max_delta)
+ timestamp_max_delta = diff;
+ /* New max delta for timestamp */
+ } else {
+ min_timestamp = timestamp;
+ have_timestamp = 1;
+ }
+ }
+ }
+ /* Destroy all feeds we finished */
+ for (src_i = 0; src_i < self->feeds_nr; src_i++)
+ if (self->feeds[src_i].mode == SFX_PCM_FEED_MODE_DEAD)
+ _mix_unsubscribe(self, self->feeds[src_i].feed);
+ } while (frames_skip >= 0);
+
+ } else { /* Zero it out */
+ memset(P->compbuf_l, 0, sizeof(gint32) * buflen);
+ memset(P->compbuf_r, 0, sizeof(gint32) * buflen);
+ }
+
+#if (DEBUG >= 1)
+ if (self->feeds_nr)
+ sciprintf("[soft-mixer] Done mixing for this session, the result will be our next output buffer\n");
+#endif
+
+#if (DEBUG >= 3)
+ if (self->feeds_nr) {
+ int i;
+ sciprintf("[soft-mixer] Intermediate representation:\n");
+ for (i = 0; i < buflen; i++)
+ sciprintf("[soft-mixer] Offset %d:\t[%04x:%04x]\t%d:%d\n", i,
+ P->compbuf_l[i] & 0xffff, P->compbuf_r[i] & 0xffff,
+ P->compbuf_l[i], P->compbuf_r[i]);
+ }
+#endif
+
+ if (timestamp_max_delta > TIMESTAMP_MAX_ALLOWED_DELTA)
+ sciprintf("[soft-mixer] Warning: Difference in timestamps between audio feeds is %d us\n", timestamp_max_delta);
+
+ mix_compute_output(self, buflen);
+ P->lastbuf_len = buflen;
+
+ /* Finalize */
+ mix_swap_buffers(self);
+ if (have_timestamp)
+ P->outbuf_timestamp = sfx_timestamp_add(min_timestamp,
+ timestamp_max_delta >> 1);
+ P->have_outbuf_timestamp = have_timestamp;
+
+} RELEASE_LOCK();
+ return SFX_OK;
+}
+
+static void
+mix_pause(sfx_pcm_mixer_t *self)
+{
+ ACQUIRE_LOCK();
+ P->paused = 1;
+ RELEASE_LOCK();
+}
+
+static void
+mix_resume(sfx_pcm_mixer_t *self)
+{
+ ACQUIRE_LOCK();
+ P->paused = 0;
+ RELEASE_LOCK();
+}
+
+sfx_pcm_mixer_t sfx_pcm_mixer_soft_linear = {
+ "soft-linear",
+ "0.1",
+
+ mix_init,
+ mix_exit,
+ mix_subscribe,
+ mix_pause,
+ mix_resume,
+ mix_process_linear,
+
+ 0,
+ 0,
+ NULL,
+ NULL,
+ NULL
+};
diff --git a/engines/sci/sfx/mixer/test.c b/engines/sci/sfx/mixer/test.c
new file mode 100644
index 0000000000..20b3e952e1
--- /dev/null
+++ b/engines/sci/sfx/mixer/test.c
@@ -0,0 +1,351 @@
+/***************************************************************************
+ test.c Copyright (C) 2003 Christoph Reichenbach
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+/* Mixer inspection/test program */
+
+
+#include "../mixer.h"
+#include <time.h>
+
+#if 0
+sfx_pcm_mixer_t *mix;
+
+int dev_init(sfx_pcm_device_t *self);
+void dev_exit(sfx_pcm_device_t *self);
+int dev_option(sfx_pcm_device_t *self, char *name, char *value);
+int dev_output(sfx_pcm_device_t *self, byte *buf, int count);
+
+#define MIN_OUTPUT 128
+/* Min amount of output to compute */
+
+#define DEVICES_NR 10
+
+sfx_pcm_device_t devices[DEVICES_NR] = {
+ { "test-1", "0", dev_init, dev_exit, dev_option, dev_output,
+ { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_U8 }, 1024, NULL },
+#if (DEVICES_NR > 1)
+ { "test-2", "0", dev_init, dev_exit, dev_option, dev_output,
+ { 200, SFX_PCM_STEREO_LR, SFX_PCM_FORMAT_U8 }, 1024, NULL },
+ { "test-3", "0", dev_init, dev_exit, dev_option, dev_output,
+ { 200, SFX_PCM_STEREO_RL, SFX_PCM_FORMAT_U8 }, 1024, NULL },
+ { "test-4", "0", dev_init, dev_exit, dev_option, dev_output,
+ { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_S8 }, 1024, NULL },
+ { "test-5", "0", dev_init, dev_exit, dev_option, dev_output,
+ { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_U16_LE }, 1024, NULL },
+ { "test-6", "0", dev_init, dev_exit, dev_option, dev_output,
+ { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_U16_BE }, 1024, NULL },
+ { "test-7", "0", dev_init, dev_exit, dev_option, dev_output,
+ { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_S16_LE }, 1024, NULL },
+ { "test-8", "0", dev_init, dev_exit, dev_option, dev_output,
+ { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_S16_BE }, 1024, NULL },
+ { "test-9", "0", dev_init, dev_exit, dev_option, dev_output,
+ { 200, SFX_PCM_STEREO_RL, SFX_PCM_FORMAT_S16_LE }, 1024, NULL },
+ { "test-10", "0", dev_init, dev_exit, dev_option, dev_output,
+ { 200, SFX_PCM_STEREO_LR, SFX_PCM_FORMAT_U16_BE }, 1024, NULL }
+#endif
+};
+
+int output_count;
+
+int dev_init(sfx_pcm_device_t *self)
+{
+ output_count = 0;
+
+ fprintf(stderr, "[DEV] Initialised device %p as follows:\n"
+ "\trate = %d\n"
+ "\tstereo = %s\n"
+ "\tbias = %x\n"
+ "\tbytes/sample = %d\n"
+ "\tendianness = %s\n",
+ self,
+ self->conf.rate,
+ self->conf.stereo? ((self->conf.stereo == SFX_PCM_STEREO_LR)? "Left, Right" : "Right, Left") : "No",
+ self->conf.format & ~SFX_PCM_FORMAT_LMASK,
+ (self->conf.format & SFX_PCM_FORMAT_16)? 2 : 1,
+ ((self->conf.format & SFX_PCM_FORMAT_ENDIANNESS) == SFX_PCM_FORMAT_BE)? "big" : "little");
+ return 0;
+}
+
+void dev_exit(sfx_pcm_device_t *self)
+{
+ fprintf(stderr, "[DEV] Uninitialising device\n");
+}
+
+int dev_option(sfx_pcm_device_t *self, char *name, char *value)
+{
+ fprintf(stderr, "[DEV] Set option '%s' to '%s'\n", name, value);
+ return 0;
+}
+
+int dev_output_enabled = 0;
+
+int dev_output(sfx_pcm_device_t *self, byte *buf, int count)
+{
+ int mono_sample_size = ((self->conf.format & SFX_PCM_FORMAT_16)? 2 : 1);
+ int sample_size = (self->conf.stereo? 2 : 1) * mono_sample_size;
+ int bias = self->conf.format & ~SFX_PCM_FORMAT_LMASK;
+ int is_bigendian = (self->conf.format & SFX_PCM_FORMAT_ENDIANNESS) == SFX_PCM_FORMAT_BE;
+ byte *left_channel = buf;
+ byte *right_channel = buf;
+
+ if (!dev_output_enabled)
+ return 0;
+
+ if (self->conf.format & SFX_PCM_FORMAT_16)
+ bias <<= 8;
+
+ if (self->conf.stereo == SFX_PCM_STEREO_LR)
+ right_channel += mono_sample_size;
+ if (self->conf.stereo == SFX_PCM_STEREO_RL)
+ left_channel += mono_sample_size;
+
+ while (count--) {
+ int right = right_channel[0];
+ int left = left_channel[0];
+ int second_byte = ((self->conf.format & SFX_PCM_FORMAT_16)? 1 : 0);
+
+ if (second_byte) {
+
+ if (is_bigendian) {
+ left = left << 8 | left_channel[1];
+ right = right << 8 | right_channel[1];
+ } else {
+ left = left | left_channel[1] << 8;
+ right = right | right_channel[1] << 8;
+ }
+ }
+
+ left -= bias;
+ right -= bias;
+
+ if (!second_byte) {
+ left <<= 8;
+ right <<= 8;
+ }
+
+ fprintf(stderr, "[DEV] %p play %04x:\t%04x %04x\n", self, output_count++, left & 0xffff, right & 0xffff);
+
+ left_channel += sample_size;
+ right_channel += sample_size;
+ }
+ return 0;
+}
+
+/* Feeds for debugging */
+
+typedef struct {
+ int i;
+} int_struct;
+
+int feed_poll(sfx_pcm_feed_t *self, byte *dest, int size);
+void feed_destroy(sfx_pcm_feed_t *self);
+
+int_struct private_bits[10] = {
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ {0},
+ {0}
+};
+
+
+typedef struct {
+ int start;
+ int samples_nr;
+ byte *data;
+} sample_feed_t;
+
+#define FEEDS_NR 4
+
+sfx_pcm_feed_t feeds[FEEDS_NR] = {
+ { feed_poll, feed_destroy, &(private_bits[0]),
+ { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_S8 }, "test-feed", 0, 0}
+#if FEEDS_NR > 1
+ ,{ feed_poll, feed_destroy, &(private_bits[1]),
+ { 400, SFX_PCM_MONO, SFX_PCM_FORMAT_U8 }, "test-feed", 1, 0}
+#endif
+#if FEEDS_NR > 2
+ ,{ feed_poll, feed_destroy, &(private_bits[2]),
+ { 20, SFX_PCM_MONO, SFX_PCM_FORMAT_S16_LE }, "test-feed", 2, 0}
+#endif
+#if FEEDS_NR > 3
+ ,{ feed_poll, feed_destroy, &(private_bits[3]),
+ { 150, SFX_PCM_STEREO_LR, SFX_PCM_FORMAT_S8 }, "test-feed", 3, 0}
+#endif
+ /*
+ ,{ feed_poll, feed_destroy, &(private_bits[4]),
+ {}, "test-feed", 4, 0}
+ ,{ feed_poll, feed_destroy, &(private_bits[5]),
+ {}, "test-feed", 5, 0}
+ */
+};
+
+byte feed_data_0[] = {0xfd, 0xfe, 0xff, 0, 1, 2, 3, 4, 5, 6};
+byte feed_data_1[] = {0x80, 0x90, 0xA0, 0xB0, 0xC0,
+ 0xD0, 0xD0, 0xC0, 0xB0, 0xA0, 0x90, 0x80};
+byte feed_data_2[] = {0x00, 0x00,
+ 0x00, 0x80,
+ 0xe8, 0x03};
+byte feed_data_3[] = {0x00, 0x10,
+ 0x01, 0x20,
+ 0x02, 0x30};
+
+sample_feed_t sample_feeds[FEEDS_NR] = {
+ { 1, 10, feed_data_0 }
+#if FEEDS_NR > 1
+ ,{ 21, 12, feed_data_1 }
+#endif
+#if FEEDS_NR > 2
+ ,{ 0, 3, feed_data_2 }
+#endif
+#if FEEDS_NR > 3
+ ,{ 40, 3, feed_data_3 }
+#endif
+};
+
+void
+feed_destroy(sfx_pcm_feed_t *self)
+{
+ int_struct *s = (int_struct *) self->internal;
+ s->i = 0; /* reset */
+}
+
+
+int
+feed_poll(sfx_pcm_feed_t *self, byte *dest, int size)
+{
+ int_struct *s = (int_struct *) self->internal;
+ int sample_size = self->sample_size;
+ sample_feed_t *data = &(sample_feeds[self->debug_nr]);
+ int bias = self->conf.format & ~SFX_PCM_FORMAT_LMASK;
+ byte neutral[4] = {0, 0, 0, 0};
+ int i;
+fprintf(stderr, "[feed] Asked for %d at %p, ss=%d\n", size, dest, sample_size);
+ if (bias) {
+ byte first = bias >> 8;
+ byte second = bias & 0xff;
+
+ if ((self->conf.format & SFX_PCM_FORMAT_ENDIANNESS) == SFX_PCM_FORMAT_LE) {
+ int t = first;
+ first = second;
+ second = t;
+ }
+
+ if (self->conf.format & SFX_PCM_FORMAT_16) {
+ neutral[0] = first;
+ neutral[1] = second;
+ neutral[2] = first;
+ neutral[3] = second;
+ } else {
+ neutral[0] = bias;
+ neutral[1] = bias;
+ }
+ }
+
+ for (i = 0; i < size; i++) {
+ int t = s->i - data->start;
+
+ if (t >= data->samples_nr)
+ return i;
+
+ if (t >= 0)
+ memcpy(dest, data->data + t * sample_size, sample_size);
+ else
+ memcpy(dest, neutral, sample_size);
+
+ dest += sample_size;
+ s->i++;
+ }
+ return size;
+}
+
+
+
+
+extern FILE *con_file;
+
+#define DELAY usleep((rand() / (RAND_MAX / 250L)))
+
+
+int
+main(int argc, char **argv)
+{
+ int dev_nr;
+
+ mix = sfx_pcm_find_mixer(NULL);
+
+ if (!mix) {
+ fprintf(stderr, "Error: Could not find a mixer!\n");
+ return 1;
+ } else {
+ fprintf(stderr, "Running %s, v%s\n",
+ mix->name, mix->version);
+ }
+ con_file = stderr;
+
+ srand(time(NULL));
+
+ for (dev_nr = 0; dev_nr < DEVICES_NR; dev_nr++) {
+ sfx_pcm_device_t *dev = &(devices[dev_nr++]);
+ int j;
+ dev->init(dev);
+ mix->init(mix, dev);
+
+ dev_output_enabled = 0;
+ /* Prime it to our timing */
+ for (j = 0; j < 250; j++) {
+ DELAY;
+ mix->process(mix);
+ }
+ dev_output_enabled = 1;
+
+ fprintf(stderr, "[test] Subscribing...\n");
+
+ for (j = 0; j < FEEDS_NR; j++)
+ mix->subscribe(mix, &(feeds[j]));
+
+ fprintf(stderr, "[test] Subscribed %d feeds.\n",
+ FEEDS_NR);
+
+ while (output_count < MIN_OUTPUT) {
+ DELAY;
+ mix->process(mix);
+ fprintf(stderr, "<tick>\n");
+ }
+
+ fprintf(stderr, "[test] Preparing finalisation\n");
+ mix->exit(mix);
+ fprintf(stderr, "[test] Mixer uninitialised\n");
+ }
+}
+
+#else
+int main() {}
+#endif
diff --git a/engines/sci/sfx/mt32_GM_mapping/Makefile b/engines/sci/sfx/mt32_GM_mapping/Makefile
new file mode 100644
index 0000000000..2d40cb25b0
--- /dev/null
+++ b/engines/sci/sfx/mt32_GM_mapping/Makefile
@@ -0,0 +1,15 @@
+CC =gcc
+CFLAGS =-O2 -Wall
+objects =main.o
+
+mtgm: $(objects)
+ $(CC) -o mtgm $(objects)
+
+main.o: main.c
+ $(CC) $(CFLAGS) -c main.c
+
+.PHONY: clean distclean
+clean:
+ rm -f mtgm $(objects)
+distclean:
+ rm -f mtgm $(objects) *~
diff --git a/engines/sci/sfx/mt32_GM_mapping/README b/engines/sci/sfx/mt32_GM_mapping/README
new file mode 100644
index 0000000000..8fa94ec4fd
--- /dev/null
+++ b/engines/sci/sfx/mt32_GM_mapping/README
@@ -0,0 +1,2 @@
+The sourcecode in this directory is not intended to be included in FreeSCI.
+
diff --git a/engines/sci/sfx/mt32_GM_mapping/gm_patches.c b/engines/sci/sfx/mt32_GM_mapping/gm_patches.c
new file mode 100644
index 0000000000..b959a87461
--- /dev/null
+++ b/engines/sci/sfx/mt32_GM_mapping/gm_patches.c
@@ -0,0 +1,198 @@
+/***************************************************************************
+ gm_patches.c Copyright (C) 2000 Rickard Lind
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+char *GM_Patch[128] = {
+/*000*/ "Acoustic Grand Piano",
+/*001*/ "Bright Acoustic Piano",
+/*002*/ "Electric Grand Piano",
+/*003*/ "Honky-tonk Piano",
+/*004*/ "Electric Piano 1",
+/*005*/ "Electric Piano 2",
+/*006*/ "Harpsichord",
+/*007*/ "Clavinet",
+/*008*/ "Celesta",
+/*009*/ "Glockenspiel",
+/*010*/ "Music Box",
+/*011*/ "Vibraphone",
+/*012*/ "Marimba",
+/*013*/ "Xylophone",
+/*014*/ "Tubular Bells",
+/*015*/ "Dulcimer",
+/*016*/ "Drawbar Organ",
+/*017*/ "Percussive Organ",
+/*018*/ "Rock Organ",
+/*019*/ "Church Organ",
+/*020*/ "Reed Organ",
+/*021*/ "Accordion",
+/*022*/ "Harmonica",
+/*023*/ "Tango Accordion",
+/*024*/ "Acoustic Guitar (nylon)",
+/*025*/ "Acoustic Guitar (steel)",
+/*026*/ "Electric Guitar (jazz)",
+/*027*/ "Electric Guitar (clean)",
+/*028*/ "Electric Guitar (muted)",
+/*029*/ "Overdriven Guitar",
+/*030*/ "Distortion Guitar",
+/*031*/ "Guitar Harmonics",
+/*032*/ "Acoustic Bass",
+/*033*/ "Electric Bass (finger)",
+/*034*/ "Electric Bass (pick)",
+/*035*/ "Fretless Bass",
+/*036*/ "Slap Bass 1",
+/*037*/ "Slap Bass 2",
+/*038*/ "Synth Bass 1",
+/*039*/ "Synth Bass 2",
+/*040*/ "Violin",
+/*041*/ "Viola",
+/*042*/ "Cello",
+/*043*/ "Contrabass",
+/*044*/ "Tremolo Strings",
+/*045*/ "Pizzicato Strings",
+/*046*/ "Orchestral Harp",
+/*047*/ "Timpani",
+/*048*/ "String Ensemble 1",
+/*049*/ "String Ensemble 2",
+/*050*/ "SynthStrings 1",
+/*051*/ "SynthStrings 2",
+/*052*/ "Choir Aahs",
+/*053*/ "Voice Oohs",
+/*054*/ "Synth Voice",
+/*055*/ "Orchestra Hit",
+/*056*/ "Trumpet",
+/*057*/ "Trombone",
+/*058*/ "Tuba",
+/*059*/ "Muted Trumpet",
+/*060*/ "French Horn",
+/*061*/ "Brass Section",
+/*062*/ "SynthBrass 1",
+/*063*/ "SynthBrass 2",
+/*064*/ "Soprano Sax",
+/*065*/ "Alto Sax",
+/*066*/ "Tenor Sax",
+/*067*/ "Baritone Sax",
+/*068*/ "Oboe",
+/*069*/ "English Horn",
+/*070*/ "Bassoon",
+/*071*/ "Clarinet",
+/*072*/ "Piccolo",
+/*073*/ "Flute",
+/*074*/ "Recorder",
+/*075*/ "Pan Flute",
+/*076*/ "Blown Bottle",
+/*077*/ "Shakuhachi",
+/*078*/ "Whistle",
+/*079*/ "Ocarina",
+/*080*/ "Lead 1 (square)",
+/*081*/ "Lead 2 (sawtooth)",
+/*082*/ "Lead 3 (calliope)",
+/*083*/ "Lead 4 (chiff)",
+/*084*/ "Lead 5 (charang)",
+/*085*/ "Lead 6 (voice)",
+/*086*/ "Lead 7 (fifths)",
+/*087*/ "Lead 8 (bass+lead)",
+/*088*/ "Pad 1 (new age)",
+/*089*/ "Pad 2 (warm)",
+/*090*/ "Pad 3 (polysynth)",
+/*091*/ "Pad 4 (choir)",
+/*092*/ "Pad 5 (bowed)",
+/*093*/ "Pad 6 (metallic)",
+/*094*/ "Pad 7 (halo)",
+/*095*/ "Pad 8 (sweep)",
+/*096*/ "FX 1 (rain)",
+/*097*/ "FX 2 (soundtrack)",
+/*098*/ "FX 3 (crystal)",
+/*099*/ "FX 4 (atmosphere)",
+/*100*/ "FX 5 (brightness)",
+/*101*/ "FX 6 (goblins)",
+/*102*/ "FX 7 (echoes)",
+/*103*/ "FX 8 (sci-fi)",
+/*104*/ "Sitar",
+/*105*/ "Banjo",
+/*106*/ "Shamisen",
+/*107*/ "Koto",
+/*108*/ "Kalimba",
+/*109*/ "Bag pipe",
+/*110*/ "Fiddle",
+/*111*/ "Shannai",
+/*112*/ "Tinkle Bell",
+/*113*/ "Agogo",
+/*114*/ "Steel Drums",
+/*115*/ "Woodblock",
+/*116*/ "Taiko Drum",
+/*117*/ "Melodic Tom",
+/*118*/ "Synth Drum",
+/*119*/ "Reverse Cymbal",
+/*120*/ "Guitar Fret Noise",
+/*121*/ "Breath Noise",
+/*122*/ "Seashore",
+/*123*/ "Bird Tweet",
+/*124*/ "Telephone Ring",
+/*125*/ "Helicopter",
+/*126*/ "Applause",
+/*127*/ "Gunshot" };
+
+char *GM_RhythmKey[47] = {
+/*035*/ "Acoustic Bass Drum",
+/*036*/ "Bass Drum 1",
+/*037*/ "Side Stick",
+/*038*/ "Acoustic Snare",
+/*039*/ "Hand Clap",
+/*040*/ "Electric Snare",
+/*041*/ "Low Floor Tom",
+/*042*/ "Closed Hi-Hat",
+/*043*/ "High Floor Tom",
+/*044*/ "Pedal Hi-Hat",
+/*045*/ "Low Tom",
+/*046*/ "Open Hi-Hat",
+/*047*/ "Low-Mid Tom",
+/*048*/ "Hi-Mid Tom",
+/*049*/ "Crash Cymbal 1",
+/*050*/ "High Tom",
+/*051*/ "Ride Cymbal 1",
+/*052*/ "Chinese Cymbal",
+/*053*/ "Ride Bell",
+/*054*/ "Tambourine",
+/*055*/ "Splash Cymbal",
+/*056*/ "Cowbell",
+/*057*/ "Crash Cymbal 2",
+/*058*/ "Vibraslap",
+/*059*/ "Ride Cymbal 2",
+/*060*/ "Hi Bongo",
+/*061*/ "Low Bongo",
+/*062*/ "Mute Hi Conga",
+/*063*/ "Open Hi Conga",
+/*064*/ "Low Conga",
+/*065*/ "High Timbale",
+/*066*/ "Low Timbale",
+/*067*/ "High Agogo",
+/*068*/ "Low Agogo",
+/*069*/ "Cabasa",
+/*070*/ "Maracas",
+/*071*/ "Short Whistle",
+/*072*/ "Long Whistle",
+/*073*/ "Short Guiro",
+/*074*/ "Long Guiro",
+/*075*/ "Claves",
+/*076*/ "Hi Wood Block",
+/*077*/ "Low Wood Block",
+/*078*/ "Mute Cuica",
+/*079*/ "Open Cuica",
+/*080*/ "Mute Triangle"
+/*081*/ "Open Triangle" };
diff --git a/engines/sci/sfx/mt32_GM_mapping/lb2map.txt b/engines/sci/sfx/mt32_GM_mapping/lb2map.txt
new file mode 100644
index 0000000000..89c60aa552
--- /dev/null
+++ b/engines/sci/sfx/mt32_GM_mapping/lb2map.txt
@@ -0,0 +1,118 @@
+ ----------------------------------------------------------------------
+ | Dagger of Amon Ra | Boop boop be doop | Wahoo! |
+--------------------------------------------------------------------------
+| ## | MT-32 Timbre | KSh FTn BR | General MIDI Patch | KSh VolA V |
+--------------------------------------------------------------------------
+| 04 | AcouPiano3 | 00 00 12 | Acoustic Grand Piano | 000 000 3 |
+| 05 | Honkytonk | 00 00 12 | Honky-tonk Piano | 000 000 3 |
+| 06 | m ClarinetMS | 00 00 12 | Clarinet | 000 -020 3 |
+| 07 | Acou Bass1 | 00 00 12 | Acoustic Bass | 000 -015 3 |
+| 08 | Trombone 2 | 00 00 12 | Tuba | 000 005 3 |
+| 09 | m StrSect1MS | 00 00 12 | String Ensemble 1 | 000 020 3 |
+| 10 | m Fantasy2MS | 00 00 12 | Pad 1 (new age) | 000 010 3 |
+| 11 | Flute 2 | 00 00 12 | Flute | 000 -020 3 |
+| 12 | Timpani | 00 00 12 | Timpani | 000 -015 1 |
+| 13 | Trumpet 1 | 00 00 12 | Trumpet | 000 030 3 |
+| 14 | m FrHorn1MS2 | 00 00 12 | French Horn | 000 000 3 |
+| 15 | m Oboe MS | 00 00 12 | Oboe | 000 -020 3 |
+| 16 | m Pizz MS | 00 00 12 | Pizzicato Strings | 000 000 3 |
+| 17 | m CymSwellMS | 00 00 12 | Reverse Cymbal | 000 000 3 |
+| 18 | Xylophone | 00 00 12 | Xylophone | 000 000 3 |
+| 19 | Bassoon | 00 00 12 | Bassoon | 000 020 3 |
+| 20 | Accordion | 00 00 12 | Accordion | 000 000 3 |
+| 21 | m BanjoLB2 | 00 00 12 | Banjo | 000 -020 3 |
+| 22 | Marimba | 00 00 12 | Marimba | 000 000 3 |
+| 23 | m WarmPadStr | 00 00 12 | String Ensemble 2 | 000 015 3 |
+| 24 | m BassPizzMS | 00 00 12 | Pizzicato Strings | -012 000 3 |
+| 25 | m WoodBlox | 00 00 12 | Woodblock | 000 000 3 |
+| 26 | Vibe 1 | 00 00 00 | Vibraphone | 000 -010 3 |
+| 27 | Sax 4 | 00 00 12 | Tenor Sax | 000 000 3 |
+| 28 | m Glock MS | 00 00 12 | Glockenspiel | 000 000 3 |
+| 29 | Koto | 00 00 12 | Koto | 000 000 3 |
+| 30 | m Taiko | 00 00 12 | Taiko Drum | 012 -010 1 |
+| 31 | Guitar 1 | 00 00 12 | Acoustic Guitar (nylon) | 000 -035 3 |
+| 32 | m Bell Tree | 00 00 12 | Glockenspiel | 000 000 3 |
+| 33 | Sitar | 00 00 12 | Sitar | 000 -010 3 |
+| 34 | Harp 1 | 00 00 12 | Orchestral Harp | 000 -025 3 |
+| 45 | m Fantasy2MS | 00 00 12 | Tubular Bells | 012 010 3 |
+| 46 | m Window | 00 00 12 | Reverse Cymbal | -048 000 3 |
+| 47 | m Snare | 00 00 00 | Woodblock | 000 000 0 |
+| 48 | m CracklesMS | 00 00 12 | Woodblock | 000 000 0 |
+| 49 | m TireSqueal | 00 00 12 | | |
+| 50 | m Gurgle | 00 00 12 | | |
+| 51 | m Toilet | 00 00 12 | | |
+| 52 | m hiss | 00 00 12 | | |
+| 53 | m IceBreakMS | 00 00 12 | | |
+| 54 | m DoorSlamMS | 00 00 12 | Woodblock | -012 000 0 |
+| 55 | m CreakyDLL1 | 00 00 12 | | |
+| 56 | m Armor MS | 00 00 12 | Agogo | 012 -020 0 |
+| 57 | m RatSqueek | 00 00 12 | Guitar Fret Noise | 012 000 3 |
+| 58 | m StoneDr MS | 00 00 12 | Reverse Cymbal | -048 000 3 |
+| 59 | m NewSplatMS | 00 00 12 | Melodic Tom | 000 000 2 |
+| 60 | m Splash MS | 00 00 12 | | |
+| 61 | m Bubbles | 00 00 12 | | |
+| 62 | m ChurchB MS | 00 00 12 | Tubular Bells | 000 000 3 |
+| 63 | m Thud MS | 00 00 12 | Taiko Drum | -012 000 2 |
+| 64 | m TYPIMG | 00 00 12 | | |
+| 65 | m Lock MS | 00 00 12 | Woodblock | 000 000 0 |
+| 66 | m Window | 00 00 12 | Reverse Cymbal | 000 000 3 |
+| 67 | m CabEngine | 00 00 12 | Tenor Sax | -060 050 3 |
+| 68 | m Ocean MS | 00 00 12 | | |
+| 69 | m Wind MS | 00 00 12 | | |
+| 70 | Telephone | 00 00 12 | Telephone Ring | 000 000 3 |
+| 71 | Bird Tweet | 00 00 12 | Bird Tweet | 000 000 3 |
+| 72 | m Explode MS | 00 00 12 | Gunshot | -012 -015 3 |
+| 73 | m SwmpBackgr | 00 00 12 | | |
+| 74 | m Toing | 00 00 12 | | |
+| 75 | m Lone Wolf | 00 00 12 | | |
+| 76 | Whistle 2 | 00 00 12 | Whistle | 000 000 3 |
+| 77 | m seagulls | 00 00 12 | Bird Tweet | 000 000 3 |
+| 78 | m Scrubin'MS | 00 00 12 | Reverse Cymbal | 000 000 3 |
+| 79 | m SqurWaveMS | 00 00 12 | Lead 1 (square) | 000 000 3 |
+| 80 | m InHale MS | 00 00 12 | Breath Noise | 000 025 3 |
+| 81 | m Arena2 MS | 00 00 12 | Applause | 000 000 3 |
+| 82 | m ArenaNoSus | 00 00 12 | Applause | 000 000 3 |
+| 92 | AcouPiano1 | 00 00 02 | | |
+| 93 | AcouPiano1 | 00 00 02 | | |
+| 94 | AcouPiano1 | 00 00 02 | | |
+| 95 | AcouPiano1 | 00 00 02 | | |
+--------------------------------------------------------------------------
+ | ## | MT-32 Timbre | OL PP | General MIDI Rhythm Key |
+ --------------------------------------------------------
+ | 35 | r Acou BD | 100 07 | Acoustic Bass Drum |
+ | 36 | r Acou BD | 100 07 | Acoustic Bass Drum |
+ | 37 | r Rim Shot | 100 06 | Side Stick |
+ | 38 | r Acou SD | 100 07 | Electric Snare |
+ | 39 | r Hand Clap | 100 08 | Hand Clap |
+ | 40 | r Acou SD | 100 06 | Electric Snare |
+ | 41 | r AcouLowTom | 100 11 | Low Floor Tom |
+ | 42 | r Clsd HiHat | 100 06 | Closed Hi-Hat |
+ | 43 | r AcouLowTom | 100 11 | High Floor Tom |
+ | 44 | r OpenHiHat2 | 100 06 | Pedal Hi-Hat |
+ | 45 | r AcouMidTom | 100 08 | Low Tom |
+ | 46 | r OpenHiHat1 | 100 06 | Open Hi-Hat |
+ | 47 | r AcouMidTom | 100 08 | Low-Mid Tom |
+ | 48 | r Acou HiTom | 100 03 | Hi-Mid Tom |
+ | 49 | r Crash Cym | 100 06 | Crash Cymbal 1 |
+ | 50 | r Acou HiTom | 100 03 | High Tom |
+ | 51 | r Ride Cym | 100 08 | Ride Cymbal 1 |
+ | 52 | m CymSwellMS | 100 07 | |
+ | 54 | r Tambourine | 100 09 | Tambourine |
+ | 55 | m ChokeCrash | 100 07 | Crash Cymbal 2 |
+ | 56 | r Cowbell | 100 07 | Cowbell |
+ | 60 | r High Bongo | 100 02 | Hi Bongo |
+ | 61 | r Low Bongo | 100 04 | Low Bongo |
+ | 62 | r Mt HiConga | 100 08 | Mute Hi Conga |
+ | 63 | r High Conga | 100 09 | Open Hi Conga |
+ | 64 | r Low Conga | 100 10 | Low Conga |
+ | 65 | r Hi Timbale | 100 07 | High Timbale |
+ | 66 | r LowTimbale | 100 05 | Low Timbale |
+ | 67 | r High Agogo | 100 02 | High Agogo |
+ | 68 | r Low Agogo | 100 02 | Low Agogo |
+ | 69 | r Cabasa | 100 09 | Cabasa |
+ | 70 | r Maracas | 100 04 | Maracas |
+ | 71 | r SmbaWhis S | 100 09 | Short Whistle |
+ | 72 | r SmbaWhis L | 100 09 | Long Whistle |
+ | 73 | r Quijada | 100 00 | |
+ | 75 | r Claves | 100 12 | Claves |
+ --------------------------------------------------------
diff --git a/engines/sci/sfx/mt32_GM_mapping/main.c b/engines/sci/sfx/mt32_GM_mapping/main.c
new file mode 100644
index 0000000000..1db2b584b4
--- /dev/null
+++ b/engines/sci/sfx/mt32_GM_mapping/main.c
@@ -0,0 +1,157 @@
+/***************************************************************************
+ main.c Copyright (C) 2000 Rickard Lind
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "mt32_timbres.c"
+#include "gm_patches.c"
+
+void analyze(unsigned char *patch001, unsigned int length001,
+ unsigned char *patch004, unsigned int length004);
+
+int main(int argc, char **argv)
+{
+ int fd1, fd2;
+ unsigned char *patch001;
+ unsigned char *patch004;
+ unsigned int length001, length004;
+
+ if (argc < 2)
+ return -1;
+
+ if ((fd1 = open(argv[1], O_RDONLY)) < 0)
+ return -1;
+ if ((fd2 = open(argv[2], O_RDONLY)) < 0) {
+ close(fd1);
+ return -1;
+ }
+
+ patch001 = (unsigned char *)sci_malloc(65536);
+ length001 = read(fd1, patch001, 65536);
+ close(fd1);
+
+ patch004 = (unsigned char *)sci_malloc(65536);
+ length004 = read(fd2, patch004, 65536);
+ close(fd2);
+
+ if (patch001[0] == 0x89 && patch001[1] == 0x00)
+ if (patch004[0] == 0x89 && patch004[1] == 0x00)
+ analyze(patch001 + 2, length001 - 2, patch004 + 2, length004 - 2);
+ else
+ analyze(patch001 + 2, length001 - 2, patch004, length004);
+ else
+ if (patch004[0] == 0x89 && patch004[1] == 0x00)
+ analyze(patch001, length001, patch004 + 2, length004 - 2);
+ else
+ analyze(patch001, length001, patch004, length004);
+
+ free(patch001);
+ free(patch004);
+
+ return 0;
+}
+
+void analyze(unsigned char *patch001, unsigned int length001,
+ unsigned char *patch004, unsigned int length004)
+{
+ int i;
+ unsigned char *mt32patch;
+ unsigned char *mt32rhythm;
+
+ printf(" ----------------------------------------------------------------------\n");
+ printf(" | %.20s | %.20s | %.20s |\n", patch001, patch001 + 20, patch001 + 40);
+ printf("--------------------------------------------------------------------------\n");
+ printf("| ## | MT-32 Timbre | KSh FTn BR | General MIDI Patch | KSh VolA V |\n");
+ printf("--------------------------------------------------------------------------\n");
+ for (i = 0; i < 96; i++) {
+ if (i < 48)
+ mt32patch = patch001 + 107 + i * 8;
+ else
+ mt32patch = patch001 + 110 + i * 8 + patch001[491] * 246;
+
+ if (!((mt32patch[0] == 0) &&
+ (mt32patch[1] == 0) &&
+ (mt32patch[2] == 0) &&
+ (mt32patch[3] == 0) &&
+ (mt32patch[4] == 0) &&
+ (mt32patch[5] == 0) &&
+ (mt32patch[6] == 0) &&
+ (mt32patch[7] == 0))) {
+ printf("| %02i |", i);
+ if (mt32patch[0] < 2)
+ if (mt32patch[0] == 0)
+ printf(" %.10s", MT32_Timbre[mt32patch[1]]);
+ else
+ printf(" %.10s", MT32_Timbre[mt32patch[1] + 64]);
+ else if (mt32patch[0] == 2)
+ printf(" m %.10s", patch001 + 492 + mt32patch[1] * 246);
+ else if (mt32patch[0] == 3)
+ printf(" r %.10s", MT32_RhythmTimbre[mt32patch[1]]);
+ printf(" | % 03i % 03i %02i | ",
+ mt32patch[2] - 24,
+ mt32patch[3] - 50,
+ mt32patch[4]);
+ if (patch004[i] != 0xFF) {
+ printf("%-23s ", GM_Patch[patch004[i]]);
+ printf("| % 04i % 04i %i |",
+ *((signed char *)(patch004) + i + 128),
+ *((signed char *)(patch004) + i + 256),
+ patch004[i + 513]);
+ } else
+ printf(" | |");
+ printf("\n");
+ }
+ }
+ printf("--------------------------------------------------------------------------\n");
+ printf(" | ## | MT-32 Timbre | OL PP | General MIDI Rhythm Key |\n");
+ printf(" --------------------------------------------------------\n");
+ for (i = 0; i < 64; i++)
+ {
+ mt32rhythm = patch001 + 880 + i * 4 + patch001[491] * 246;
+ if ((mt32rhythm[0] < 94) &&
+ !((mt32rhythm[0] == 0) &&
+ (mt32rhythm[1] == 0) &&
+ (mt32rhythm[2] == 0) &&
+ (mt32rhythm[3] == 0)) &&
+ !((mt32rhythm[0] == 1) &&
+ (mt32rhythm[1] == 1) &&
+ (mt32rhythm[2] == 1) &&
+ (mt32rhythm[3] == 1))) {
+ printf(" | %02i |", i + 24);
+ if (mt32rhythm[0] < 64)
+ printf(" m %.10s", patch001 + 492 + mt32rhythm[0] * 246);
+ else
+ printf(" r %.10s", MT32_RhythmTimbre[mt32rhythm[0] - 64]);
+ printf(" | %03i %02i | ", mt32rhythm[1], mt32rhythm[2]);
+ if (patch004[384 + i + 24] != 0xFF)
+ printf("%-23s |", GM_RhythmKey[patch004[384 + i + 24] - 35]);
+ else
+ printf(" |");
+ printf("\n");
+ }
+ }
+ printf(" --------------------------------------------------------\n");
+ return;
+}
diff --git a/engines/sci/sfx/mt32_GM_mapping/mt32_timbres.c b/engines/sci/sfx/mt32_GM_mapping/mt32_timbres.c
new file mode 100644
index 0000000000..beb5fa97e3
--- /dev/null
+++ b/engines/sci/sfx/mt32_GM_mapping/mt32_timbres.c
@@ -0,0 +1,181 @@
+/***************************************************************************
+ mt32_timbres.c Copyright (C) 2000 Rickard Lind
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+char *MT32_Timbre[128] = {
+/*000*/ "AcouPiano1",
+/*001*/ "AcouPiano2",
+/*002*/ "AcouPiano3",
+/*003*/ "ElecPiano1",
+/*004*/ "ElecPiano2",
+/*005*/ "ElecPiano3",
+/*006*/ "ElecPiano4",
+/*007*/ "Honkytonk ",
+/*008*/ "Elec Org 1",
+/*009*/ "Elec Org 2",
+/*010*/ "Elec Org 3",
+/*011*/ "Elec Org 4",
+/*012*/ "Pipe Org 1",
+/*013*/ "Pipe Org 2",
+/*014*/ "Pipe Org 3",
+/*015*/ "Accordion ",
+/*016*/ "Harpsi 1 ",
+/*017*/ "Harpsi 2 ",
+/*018*/ "Harpsi 3 ",
+/*019*/ "Clavi 1 ",
+/*020*/ "Clavi 2 ",
+/*021*/ "Clavi 3 ",
+/*022*/ "Celesta 1 ",
+/*023*/ "Celesta 2 ",
+/*024*/ "Syn Brass1",
+/*025*/ "Syn Brass2",
+/*026*/ "Syn Brass3",
+/*027*/ "Syn Brass4",
+/*028*/ "Syn Bass 1",
+/*029*/ "Syn Bass 2",
+/*030*/ "Syn Bass 3",
+/*031*/ "Syn Bass 4",
+/*032*/ "Fantasy ",
+/*033*/ "Harmo Pan ",
+/*034*/ "Chorale ",
+/*035*/ "Glasses ",
+/*036*/ "Soundtrack",
+/*037*/ "Atmosphere",
+/*038*/ "Warm Bell ",
+/*039*/ "Funny Vox ",
+/*040*/ "Echo Bell ",
+/*041*/ "Ice Rain ",
+/*042*/ "Oboe 2001 ",
+/*043*/ "Echo Pan ",
+/*044*/ "DoctorSolo",
+/*045*/ "Schooldaze",
+/*046*/ "BellSinger",
+/*047*/ "SquareWave",
+/*048*/ "Str Sect 1",
+/*049*/ "Str Sect 2",
+/*050*/ "Str Sect 3",
+/*051*/ "Pizzicato ",
+/*052*/ "Violin 1 ",
+/*053*/ "Violin 2 ",
+/*054*/ "Cello 1 ",
+/*055*/ "Cello 2 ",
+/*056*/ "Contrabass",
+/*057*/ "Harp 1 ",
+/*058*/ "Harp 2 ",
+/*059*/ "Guitar 1 ",
+/*060*/ "Guitar 2 ",
+/*061*/ "Elec Gtr 1",
+/*062*/ "Elec Gtr 2",
+/*063*/ "Sitar ",
+/*064*/ "Acou Bass1",
+/*065*/ "Acou Bass2",
+/*066*/ "Elec Bass1",
+/*067*/ "Elec Bass2",
+/*068*/ "Slap Bass1",
+/*069*/ "Slap Bass2",
+/*070*/ "Fretless 1",
+/*071*/ "Fretless 2",
+/*072*/ "Flute 1 ",
+/*073*/ "Flute 2 ",
+/*074*/ "Piccolo 1 ",
+/*075*/ "Piccolo 2 ",
+/*076*/ "Recorder ",
+/*077*/ "Panpipes ",
+/*078*/ "Sax 1 ",
+/*079*/ "Sax 2 ",
+/*080*/ "Sax 3 ",
+/*081*/ "Sax 4 ",
+/*082*/ "Clarinet 1",
+/*083*/ "Clarinet 2",
+/*084*/ "Oboe ",
+/*085*/ "Engl Horn ",
+/*086*/ "Bassoon ",
+/*087*/ "Harmonica ",
+/*088*/ "Trumpet 1 ",
+/*089*/ "Trumpet 2 ",
+/*090*/ "Trombone 1",
+/*091*/ "Trombone 2",
+/*092*/ "Fr Horn 1 ",
+/*093*/ "Fr Horn 2 ",
+/*094*/ "Tuba ",
+/*095*/ "Brs Sect 1",
+/*096*/ "Brs Sect 2",
+/*097*/ "Vibe 1 ",
+/*098*/ "Vibe 2 ",
+/*099*/ "Syn Mallet",
+/*100*/ "Wind Bell ",
+/*101*/ "Glock ",
+/*102*/ "Tube Bell ",
+/*103*/ "Xylophone ",
+/*104*/ "Marimba ",
+/*105*/ "Koto ",
+/*106*/ "Sho ",
+/*107*/ "Shakuhachi",
+/*108*/ "Whistle 1 ",
+/*109*/ "Whistle 2 ",
+/*110*/ "BottleBlow",
+/*111*/ "BreathPipe",
+/*112*/ "Timpani ",
+/*113*/ "MelodicTom",
+/*114*/ "Deep Snare",
+/*115*/ "Elec Perc1",
+/*116*/ "Elec Perc2",
+/*117*/ "Taiko ",
+/*118*/ "Taiko Rim ",
+/*119*/ "Cymbal ",
+/*120*/ "Castanets ",
+/*121*/ "Triangle ",
+/*122*/ "Orche Hit ",
+/*123*/ "Telephone ",
+/*124*/ "Bird Tweet",
+/*125*/ "OneNoteJam",
+/*126*/ "WaterBells",
+/*127*/ "JungleTune" };
+
+char *MT32_RhythmTimbre[30] = {
+/*00*/ "Acou BD ",
+/*01*/ "Acou SD ",
+/*02*/ "Acou HiTom",
+/*03*/ "AcouMidTom",
+/*04*/ "AcouLowTom",
+/*05*/ "Elec SD ",
+/*06*/ "Clsd HiHat",
+/*07*/ "OpenHiHat1",
+/*08*/ "Crash Cym ",
+/*09*/ "Ride Cym ",
+/*10*/ "Rim Shot ",
+/*11*/ "Hand Clap ",
+/*12*/ "Cowbell ",
+/*13*/ "Mt HiConga",
+/*14*/ "High Conga",
+/*15*/ "Low Conga ",
+/*16*/ "Hi Timbale",
+/*17*/ "LowTimbale",
+/*18*/ "High Bongo",
+/*19*/ "Low Bongo ",
+/*20*/ "High Agogo",
+/*21*/ "Low Agogo ",
+/*22*/ "Tambourine",
+/*23*/ "Claves ",
+/*24*/ "Maracas ",
+/*25*/ "SmbaWhis L",
+/*26*/ "SmbaWhis S",
+/*27*/ "Cabasa ",
+/*28*/ "Quijada ",
+/*29*/ "OpenHiHat2" };
diff --git a/engines/sci/sfx/mt32_GM_mapping/pq1map.txt b/engines/sci/sfx/mt32_GM_mapping/pq1map.txt
new file mode 100644
index 0000000000..b2040b54f7
--- /dev/null
+++ b/engines/sci/sfx/mt32_GM_mapping/pq1map.txt
@@ -0,0 +1,135 @@
+ ----------------------------------------------------------------------
+ | ..THE DEATH ANGEL.. | POLICE QUEST I | <THANKS FOR PLAYING> |
+--------------------------------------------------------------------------
+| ## | MT-32 Timbre | KSh FTn BR | General MIDI Patch | KSh VolA V |
+--------------------------------------------------------------------------
+| 00 | m FEEDBAK AX | 00 00 12 | Distortion Guitar | -012 065 3 |
+| 01 | m REV CYMBAL | 00 00 12 | Reverse Cymbal | 000 000 0 |
+| 02 | m ANALOG SYN | 00 00 12 | Pad 3 (polysynth) | 000 127 3 |
+| 03 | m STACKBASS | 00 00 12 | Slap Bass 2 | -012 100 3 |
+| 04 | m ORGAN B | 00 00 12 | Drawbar Organ | 000 055 3 |
+| 05 | Syn Mallet | 00 00 12 | Dulcimer | 000 000 0 |
+| 06 | m HARD RIDE | 00 00 12 | | |
+| 07 | Orche Hit | 00 00 12 | Orchestra Hit | 000 000 0 |
+| 08 | m HEFTY BASS | 00 00 12 | Electric Bass (finger) | -024 100 3 |
+| 09 | AcouPiano1 | 00 00 12 | Acoustic Grand Piano | 000 000 0 |
+| 10 | m SpaceVibes | 00 00 12 | Vibraphone | 000 000 0 |
+| 11 | m CIGARETTE | -24 00 12 | | |
+| 12 | AcouPiano1 | 00 00 12 | Acoustic Grand Piano | 000 000 0 |
+| 13 | Castanets | 00 00 12 | | |
+| 14 | AcouPiano1 | 00 00 12 | Acoustic Grand Piano | 000 000 0 |
+| 15 | m LUSH STRNG | 00 00 12 | String Ensemble 1 | 000 117 3 |
+| 16 | Fantasy | 00 00 12 | Pad 1 (new age) | 000 000 0 |
+| 17 | AcouPiano1 | 00 00 12 | Acoustic Grand Piano | 000 000 0 |
+| 18 | m BIG BANJO | 00 00 12 | FX 3 (crystal) | 000 -010 3 |
+| 19 | Soundtrack | 00 00 12 | FX 2 (soundtrack) | 000 000 0 |
+| 20 | Timpani | 00 00 12 | Timpani | 000 000 0 |
+| 21 | Fr Horn 1 | 00 00 12 | French Horn | 000 000 0 |
+| 22 | Trumpet 2 | 00 00 12 | Trumpet | 000 000 3 |
+| 23 | m SMOKING | 00 00 12 | | |
+| 24 | m F VoxStrg | 00 00 12 | String Ensemble 1 | 000 000 0 |
+| 25 | m SqurWaveMS | 00 00 12 | | |
+| 26 | m TYPIMG | 00 00 12 | | |
+| 27 | m SnglVox MS | 00 00 12 | | |
+| 28 | Str Sect 2 | 00 00 12 | String Ensemble 1 | 000 000 0 |
+| 29 | Guitar 2 | 00 00 12 | Electric Guitar (clean) | 000 000 0 |
+| 30 | Elec Gtr 2 | 00 00 12 | Electric Guitar (clean) | 000 000 0 |
+| 31 | Harmonica | 00 00 12 | Harmonica | 000 000 0 |
+| 35 | Sax 3 | 00 00 12 | Alto Sax | 000 000 0 |
+| 36 | Slap Bass1 | 00 00 12 | Slap Bass 1 | 000 000 0 |
+| 37 | Fretless 1 | 00 00 12 | Fretless Bass | 000 000 0 |
+| 38 | Acou Bass1 | 00 00 12 | Acoustic Bass | 000 000 0 |
+| 41 | r Crash Cym | 00 00 12 | | |
+| 42 | r Acou BD | 00 00 12 | | |
+| 43 | m RAP SNARE | 00 00 12 | | |
+| 44 | m BIG SNARE | 00 00 00 | | |
+| 45 | m CLOSED HAT | 00 00 12 | | |
+| 46 | m SLOSH HAT | 00 00 12 | | |
+| 47 | m Gun MS | 00 00 12 | Gunshot | 000 000 0 |
+| 48 | m IceBreakMS | 00 00 12 | | |
+| 49 | m SHOWER | 00 00 12 | | |
+| 50 | m Pft MS | 00 00 12 | | |
+| 51 | m Chicago MS | 00 00 12 | Bright Acoustic Piano | 000 030 0 |
+| 52 | m ROCK GUIT1 | 00 00 12 | Distortion Guitar | -012 127 3 |
+| 53 | m Dog MS | 00 00 12 | | |
+| 54 | m TriangleMS | 00 00 12 | | |
+| 55 | m CLICKS | 00 00 12 | Woodblock | 000 000 0 |
+| 56 | Bird Tweet | 00 00 12 | Bird Tweet | 000 000 0 |
+| 57 | AcouPiano1 | 00 00 12 | Acoustic Grand Piano | 000 000 0 |
+| 58 | m CARHORN3+4 | 00 00 12 | | |
+| 59 | m CUFFS | 00 00 12 | | |
+| 60 | m ELEC PHONE | 00 00 12 | | |
+| 61 | m KongHit | 00 00 12 | | |
+| 62 | m SCUFFLE | 00 00 12 | | |
+| 63 | m FUNK PING | 00 00 12 | Orchestral Harp | 027 -010 0 |
+| 64 | m BIG TOMS | 00 00 12 | Taiko Drum | 000 000 0 |
+| 65 | m ElecGtr MS | 00 00 12 | Electric Guitar (clean) | -012 -005 3 |
+| 66 | m Lock MS | 00 00 12 | | |
+| 67 | Fretless 1 | 00 00 02 | Fretless Bass | 000 000 0 |
+| 70 | m Armor MS | 00 00 12 | | |
+| 71 | m ElecGtr MS | 00 00 02 | Electric Guitar (clean) | 000 000 0 |
+| 77 | Telephone | 00 00 00 | Telephone Ring | 000 000 0 |
+| 78 | m StoneDr MS | 00 00 12 | | |
+| 79 | m Shuffle | 00 00 12 | | |
+| 80 | m Buzzer | 00 00 12 | | |
+| 81 | Vibe 1 | 00 00 12 | Vibraphone | 000 000 0 |
+| 82 | m ElecGtr MS | 00 00 12 | Electric Guitar (clean) | 000 000 0 |
+| 83 | r Hand Clap | 00 00 12 | | |
+| 84 | m Drip MS | 00 00 12 | | |
+| 85 | m CracklesMS | 00 00 12 | | |
+| 86 | m TireSqueal | 00 00 12 | Bag pipe | 000 025 3 |
+| 87 | m Explode MS | 00 00 12 | | |
+| 88 | m DRAWER | 00 00 12 | | |
+| 89 | m KABOOM | 00 00 12 | | |
+| 90 | m GUN/STATIC | 00 00 12 | | |
+| 91 | m TRAFFIC | 00 00 12 | Seashore | 000 000 0 |
+| 92 | m DOOR SLAM | 00 00 12 | | |
+| 93 | m CAR DOOR | 00 00 12 | | |
+| 94 | m STEPS | 00 00 12 | | |
+| 95 | m DIESEL | 00 00 12 | | |
+--------------------------------------------------------------------------
+ | ## | MT-32 Timbre | OL PP | General MIDI Rhythm Key |
+ --------------------------------------------------------
+ | 34 | r Acou SD | 100 07 | Electric Snare |
+ | 35 | r Acou BD | 100 07 | Acoustic Bass Drum |
+ | 36 | m BIG KICK | 095 07 | Bass Drum 1 |
+ | 37 | r Rim Shot | 100 06 | Side Stick |
+ | 38 | m RAP SNARE | 100 07 | Electric Snare |
+ | 39 | m SLAPSTICK | 096 08 | Hand Clap |
+ | 40 | m BIG SNARE | 100 06 | Acoustic Snare |
+ | 41 | m ACOU TOMS | 080 07 | Low Floor Tom |
+ | 42 | m CLOSED HAT | 090 06 | Closed Hi-Hat |
+ | 43 | m ACOU TOMS | 080 11 | High Floor Tom |
+ | 44 | m SLOSH HAT | 082 06 | Pedal Hi-Hat |
+ | 45 | m ACOU TOMS | 085 13 | Low Tom |
+ | 46 | m CHOKE HAT | 072 06 | Open Hi-Hat |
+ | 47 | m ACOU TOMS | 080 08 | Low-Mid Tom |
+ | 48 | m ACOU TOMS | 069 06 | Hi-Mid Tom |
+ | 49 | r Crash Cym | 095 06 | Crash Cymbal 1 |
+ | 50 | m HARD RIDE | 100 08 | Ride Cymbal 2 |
+ | 51 | m RIDE CYM | 090 08 | Ride Cymbal 1 |
+ | 52 | m CRASH CYM | 077 11 | Crash Cymbal 1 |
+ | 53 | m HEFTY BASS | 082 04 | |
+ | 54 | r Tambourine | 100 09 | Tambourine |
+ | 56 | r Cowbell | 100 07 | Cowbell |
+ | 57 | m StoneDr MS | 100 07 | |
+ | 58 | r Tambourine | 100 09 | Tambourine |
+ | 60 | r High Bongo | 100 02 | Hi Bongo |
+ | 61 | r Low Bongo | 100 04 | Low Bongo |
+ | 62 | r Mt HiConga | 100 08 | Mute Hi Conga |
+ | 63 | r High Conga | 100 09 | Open Hi Conga |
+ | 64 | r Low Conga | 100 10 | Low Conga |
+ | 65 | r Hi Timbale | 100 07 | High Timbale |
+ | 66 | r LowTimbale | 100 05 | Low Timbale |
+ | 67 | r High Agogo | 100 02 | High Agogo |
+ | 68 | r Low Agogo | 100 02 | Low Agogo |
+ | 69 | r Cabasa | 100 09 | Cabasa |
+ | 70 | r Maracas | 100 04 | Maracas |
+ | 71 | r SmbaWhis S | 100 09 | Short Whistle |
+ | 72 | r SmbaWhis L | 100 09 | Long Whistle |
+ | 73 | r Quijada | 100 10 | Short Guiro |
+ | 75 | r Claves | 100 12 | Claves |
+ | 76 | m Stir | 100 07 | Long Guiro |
+ | 77 | m Hit | 100 07 | Acoustic Snare |
+ | 78 | m Brushcym | 090 07 | Ride Cymbal 1 |
+ --------------------------------------------------------
diff --git a/engines/sci/sfx/mt32_GM_mapping/qfg1map.txt b/engines/sci/sfx/mt32_GM_mapping/qfg1map.txt
new file mode 100644
index 0000000000..c1816786b1
--- /dev/null
+++ b/engines/sci/sfx/mt32_GM_mapping/qfg1map.txt
@@ -0,0 +1,123 @@
+ ----------------------------------------------------------------------
+ | *QUEST FOR GLORY I* | So U Want 2 B A HERO | QUEST FOR GLORY I |
+--------------------------------------------------------------------------
+| ## | MT-32 Timbre | KSh FTn BR | General MIDI Patch | KSh VolA V |
+--------------------------------------------------------------------------
+| 03 | m Toms MS | 00 00 12 | Melodic Tom | 000 000 1 |
+| 04 | Trumpet 2 | 00 00 12 | Trumpet | 000 022 2 |
+| 05 | m T-Bone2 MS | 00 00 12 | Trombone | 000 065 2 |
+| 06 | m FrHorn1 MS | 00 00 12 | French Horn | 000 040 3 |
+| 07 | m StrSect1MS | 00 00 12 | String Ensemble 1 | 000 127 2 |
+| 08 | Timpani | 00 00 12 | Timpani | 000 000 2 |
+| 09 | Contrabass | 00 00 12 | Contrabass | 000 080 2 |
+| 10 | m Glock MS | 00 00 12 | Glockenspiel | 012 080 3 |
+| 11 | m Flute MS | 00 00 12 | Flute | 000 045 3 |
+| 12 | Tuba | 00 00 12 | Tuba | 000 127 3 |
+| 13 | Brs Sect 1 | 00 00 12 | Brass Section | 000 100 3 |
+| 14 | Fr Horn 1 | 00 00 12 | French Horn | 000 040 3 |
+| 15 | Pizzicato | 00 00 12 | Pizzicato Strings | 000 000 2 |
+| 17 | Acou Bass1 | -12 00 00 | Acoustic Bass | 000 050 2 |
+| 18 | Sax 2 | 00 00 00 | Alto Sax | 000 040 1 |
+| 19 | m EnglHornMS | 00 00 12 | English Horn | 012 040 3 |
+| 20 | Pipe Org 1 | 00 00 00 | Church Organ | 000 000 3 |
+| 21 | Panpipes | 00 00 12 | Pan Flute | 000 020 1 |
+| 22 | Clarinet 1 | 00 00 12 | Clarinet | 000 020 3 |
+| 23 | m BassPizzMS | 00 00 12 | Orchestral Harp | -012 050 1 |
+| 24 | m Conga MS | 00 00 12 | Taiko Drum | 000 000 1 |
+| 25 | Sitar | 00 00 12 | Sitar | 000 080 3 |
+| 26 | m Snare MS | 00 00 00 | Taiko Drum | 000 000 1 |
+| 27 | m ClarinetMS | 00 00 12 | Clarinet | 000 020 3 |
+| 28 | m Fantasy2MS | 00 00 12 | Pad 4 (choir) | 000 050 1 |
+| 29 | Guitar 1 | 00 00 12 | Acoustic Guitar (nylon) | 000 000 1 |
+| 30 | Chorale | 00 00 12 | Pad 4 (choir) | 000 020 2 |
+| 31 | Syn Bass 1 | 00 00 12 | Synth Bass 1 | 000 020 1 |
+| 32 | m GameSnd MS | 00 00 12 | Lead 1 (square) | 000 000 1 |
+| 33 | m Calliope | 00 00 12 | Drawbar Organ | 000 000 1 |
+| 34 | Whistle 1 | 00 00 12 | Whistle | 000 040 3 |
+| 47 | m SKATE2 | 00 00 24 | | |
+| 48 | m Claw MS | 00 00 12 | Synth Drum | 000 000 1 |
+| 49 | m Flames MS | 00 00 12 | Breath Noise | 000 000 1 |
+| 50 | m Swords MS | 00 00 12 | | |
+| 51 | m Armor MS | 00 00 12 | | |
+| 52 | m Splat MS | 00 00 00 | Synth Drum | 000 000 1 |
+| 53 | m Fall MS | 00 00 00 | | |
+| 54 | m Crash MS | 00 00 00 | Gunshot | 000 127 3 |
+| 55 | m Laser MS | 00 00 00 | Lead 2 (sawtooth) | 000 -040 1 |
+| 56 | m Vase MS | 00 00 00 | Gunshot | -012 000 1 |
+| 57 | m Spit MS | 00 00 12 | Woodblock | -012 000 1 |
+| 58 | m Bubbles | 00 00 12 | | |
+| 59 | m Ninga MS | 00 00 12 | Breath Noise | 000 100 1 |
+| 60 | m Flame2 MS | 00 00 12 | Breath Noise | 000 100 1 |
+| 61 | m CracklesMS | 00 00 12 | Woodblock | 000 040 1 |
+| 62 | m Gulp MS | 00 00 12 | | |
+| 63 | m KnifeStkMS | 00 00 12 | Woodblock | 000 110 1 |
+| 64 | m Pft MS | 00 00 12 | Helicopter | 000 110 1 |
+| 65 | m Poof MS | 00 00 12 | | |
+| 66 | Bird Tweet | 00 00 12 | Bird Tweet | 000 000 1 |
+| 67 | m Owl MS | 00 00 12 | Bird Tweet | -012 000 1 |
+| 68 | m Wind MS | 00 00 12 | | |
+| 69 | m Cricket | 00 00 12 | Guitar Fret Noise | 000 000 1 |
+| 70 | m SwmpBackgr | 00 00 12 | Guitar Fret Noise | 000 000 1 |
+| 71 | m FireDartMS | 00 00 00 | Seashore | 000 120 1 |
+| 72 | m LghtboltMS | 00 00 12 | Seashore | 000 120 1 |
+| 73 | m Raspbry MS | 00 00 12 | Lead 2 (sawtooth) | 000 000 1 |
+| 74 | m Explode MS | 00 00 13 | Helicopter | 000 000 1 |
+| 75 | m Lock MS | 00 00 12 | Gunshot | 080 000 1 |
+| 76 | m Buildup MS | 00 00 12 | | |
+| 77 | m Boing MS | 00 00 24 | Taiko Drum | 000 000 1 |
+| 78 | m Flames3 MS | 00 00 12 | Helicopter | 000 080 1 |
+| 79 | m Thunder MS | 00 00 12 | Helicopter | -012 100 1 |
+| 80 | m Meeps MS | 00 00 12 | SynthBrass 1 | 000 000 1 |
+| 81 | m Bells MS | 00 00 12 | Tinkle Bell | 012 080 1 |
+| 82 | m Skid MS | 00 00 12 | Helicopter | 000 000 1 |
+| 83 | m RimShot MS | 00 00 12 | Woodblock | 000 000 1 |
+| 84 | m WtrFall MS | 00 00 12 | | |
+| 85 | m DoorSlamMS | 00 00 12 | Taiko Drum | 000 000 1 |
+| 86 | m SmileFacMS | 00 00 12 | Helicopter | 000 040 1 |
+| 87 | m Tumble MS | 00 00 12 | Synth Drum | 000 040 1 |
+| 88 | m Punch MS | 00 00 12 | Synth Drum | 000 080 1 |
+| 89 | m CreakyD MS | 00 00 12 | | |
+| 90 | m CstlGateMS | 00 00 12 | Gunshot | -050 050 1 |
+| 91 | m Hammer MS | 00 00 12 | | |
+| 92 | m Horse1 MS | 00 00 12 | Woodblock | 000 000 1 |
+| 93 | m Horse2 MS | 00 00 12 | Woodblock | 000 000 1 |
+| 94 | m Kiss MS | 00 00 12 | Gunshot | 100 000 1 |
+| 95 | m StrSect1MS | 00 00 12 | String Ensemble 1 | 000 127 2 |
+--------------------------------------------------------------------------
+ | ## | MT-32 Timbre | OL PP | General MIDI Rhythm Key |
+ --------------------------------------------------------
+ | 36 | r Acou BD | 100 07 | Bass Drum 1 |
+ | 37 | r Rim Shot | 100 06 | Side Stick |
+ | 38 | r Acou SD | 100 07 | Acoustic Snare |
+ | 39 | r Hand Clap | 100 08 | Hand Clap |
+ | 40 | r Elec SD | 100 06 | Electric Snare |
+ | 41 | r AcouLowTom | 100 11 | Low Floor Tom |
+ | 42 | r Clsd HiHat | 100 06 | Closed Hi-Hat |
+ | 43 | r AcouLowTom | 100 11 | Low Floor Tom |
+ | 44 | r OpenHiHat2 | 100 06 | Open Hi-Hat |
+ | 45 | r AcouMidTom | 100 08 | Low-Mid Tom |
+ | 46 | r OpenHiHat1 | 100 06 | Open Hi-Hat |
+ | 47 | r AcouMidTom | 100 08 | Hi-Mid Tom |
+ | 48 | r Acou HiTom | 100 03 | High Tom |
+ | 49 | r Crash Cym | 100 06 | Crash Cymbal 1 |
+ | 50 | r Acou HiTom | 100 03 | High Tom |
+ | 51 | r Ride Cym | 100 08 | Ride Cymbal 1 |
+ | 52 | m CymSwellMS | 100 07 | |
+ | 54 | r Tambourine | 100 09 | Tambourine |
+ | 56 | r Cowbell | 100 07 | Cowbell |
+ | 60 | r High Bongo | 100 02 | Hi Bongo |
+ | 61 | r Low Bongo | 100 04 | Low Bongo |
+ | 62 | r Mt HiConga | 100 08 | Mute Hi Conga |
+ | 63 | r High Conga | 100 09 | Open Hi Conga |
+ | 64 | r Low Conga | 100 10 | Low Conga |
+ | 65 | r Hi Timbale | 100 07 | High Timbale |
+ | 66 | r LowTimbale | 100 05 | Low Timbale |
+ | 67 | r High Agogo | 100 02 | High Agogo |
+ | 68 | r Low Agogo | 100 02 | Low Agogo |
+ | 69 | r Cabasa | 100 09 | Cabasa |
+ | 70 | r Maracas | 100 04 | Maracas |
+ | 71 | r SmbaWhis S | 100 09 | Short Whistle |
+ | 72 | r SmbaWhis L | 100 09 | Long Whistle |
+ | 73 | r Quijada | 100 00 | Short Guiro |
+ | 75 | r Claves | 100 12 | Claves |
+ --------------------------------------------------------
diff --git a/engines/sci/sfx/old/Makefile b/engines/sci/sfx/old/Makefile
new file mode 100644
index 0000000000..f7235d945b
--- /dev/null
+++ b/engines/sci/sfx/old/Makefile
@@ -0,0 +1,24 @@
+CC = gcc
+CFLAGS = -O2 -Wall
+LIBS = -L/usr/lib -lasound
+objects = main.o midi_mt32.o midiout.o midiout_unixraw.o midiout_alsaraw.o
+
+sciplaymidi: $(objects)
+ $(CC) $(LIBS) -o sciplaymidi $(objects)
+
+main.o: main.c
+ $(CC) $(CFLAGS) -c main.c
+midi_mt32.o: midi_mt32.c midi_mt32.h midiout.h
+ $(CC) $(CFLAGS) -c midi_mt32.c
+midiout.o: midiout.c midiout.h midiout_unixraw.h midiout_alsaraw.h
+ $(CC) $(CFLAGS) -c midiout.c
+midiout_unixraw.o: midiout_unixraw.c midiout_unixraw.h
+ $(CC) $(CFLAGS) -c midiout_unixraw.c
+midiout_alsaraw.o: midiout_alsaraw.c midiout_alsaraw.h
+ $(CC) $(CFLAGS) -c midiout_alsaraw.c
+
+.PHONY: clean distclean
+clean:
+ rm -f sciplaymidi $(objects)
+distclean:
+ rm -f sciplaymidi $(objects) *~
diff --git a/engines/sci/sfx/old/README b/engines/sci/sfx/old/README
new file mode 100644
index 0000000000..c652175761
--- /dev/null
+++ b/engines/sci/sfx/old/README
@@ -0,0 +1,6 @@
+type 'make' and './sciplaymidi' to program your MT-32 with the file
+'patch.001' in the current directory
+
+Only tested with Linux and ALSA...
+
+Rickard Lind <rpl@dd.chalmers.se>
diff --git a/engines/sci/sfx/old/ROADMAP b/engines/sci/sfx/old/ROADMAP
new file mode 100644
index 0000000000..9a6ec1b4da
--- /dev/null
+++ b/engines/sci/sfx/old/ROADMAP
@@ -0,0 +1,72 @@
+The way I would have made things if I had the time to do it
+Rickard Lind <rpl@dd.chalmers.se>, 2000-12-30
+-------------------------------------------------------------------------------
+
+Step 1:
+
+D rename "src/sound/midi.c" in freesci to "oldmidi.c" and still use it
+D move my "midi*" and "midiout*" to "src/sound"
+D change all "glib.h" to the real thing
+
+Step 2:
+
+D implement all note-playing, volume changing, reverb etc in "midi_mt32.*"
+ use disassembled sierra SCI1.1 driver as the main source of inspiration
+D change "soundserver_null.c" to use the new device driver for MT-32
+* use "~/.freesci/config" to set ALSA/OSS and other options
+
+Step 3:
+
+* Implement a GM translation driver "midi_gm.*" using a parsed textfile
+ for instrument mapping
+* Improve instrument mappings using new features such as keyshift,
+ volume adjust and velocity remap
+
+Step 4:
+
+* Reimplement a SCI0 soundserver using the new sound sub sytem
+* PCM support (samples) with ALSA and possibly DirectX
+* MPU-401 UART midiout driver for DOS/Windows
+
+Step 5:
+
+* SCI01, SCI1, SCI32 soundserver
+* Adlib support "midi_opl2.*", "oplout_alsaraw.*", "oplout_pcmemu.*"
+* PCM support Sound Blaster DOS
+
+Step 6:
+
+* Make it possible to play samples and use opl2 emulation (found in MAME
+ I think) at the same time through the same sound device
+
+Step 7:
+
+* All those other little nifty things...
+
+-------------------------------------------------------------------------------
+
+My idea concerning naming of the files:
+
+src/sound/midi.* wrapper for MIDI device drivers
+src/sound/midiout.* wrapper for different methods to output
+ MIDI data
+src/sound/pcm.* wrapper for PCM device drivers
+src/sound/pcmout.* wrapper for different methods to output
+ PCM data
+src/sound/midi_mt32.* Roland MT-32 device driver
+src/sound/midi_opl2.* Adlib/OPL2 device driver
+src/sound/midiout_alsaraw.* rawmidi output using alsalib
+src/sound/midiout_unixraw.* rawmidi output using unix filesystem
+src/sound/oplout_alsaraw.* opl output using alsa
+
+-------------------------------------------------------------------------------
+
+Use Linux and ALSA 0.5.x (or later) and Roland MT-32 while developing.
+Don't implement supremely stupid abstract frameworks without consulting
+experienced people on the mailinglist first. There are infinite ways to
+implement the sound subsystem the VERY VERY WRONG way. Don't make
+everything too much of a hack either.
+
+Use the files is in "lists/" when doing text output for debugging purposes.
+
+Good luck!
diff --git a/engines/sci/sfx/old/main.c b/engines/sci/sfx/old/main.c
new file mode 100644
index 0000000000..73daebd1bb
--- /dev/null
+++ b/engines/sci/sfx/old/main.c
@@ -0,0 +1,49 @@
+/***************************************************************************
+ main.c Copyright (C) 2000 Rickard Lind
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "midi_mt32.h"
+
+int main()
+{
+ int fd;
+ unsigned char *patch;
+ unsigned int length;
+
+ patch = (unsigned char *)sci_malloc(65536);
+
+ fd = open("patch.001", O_RDONLY);
+ length = read(fd, patch, 65536);
+ close(fd);
+
+ if (patch[0] == 0x89 && patch[1] == 0x00)
+ midi_mt32_open(patch + 2, length - 2);
+ else
+ midi_mt32_open(patch, length);
+
+ midi_mt32_close();
+
+ free(patch);
+ return 0;
+}
diff --git a/engines/sci/sfx/old/midi.c b/engines/sci/sfx/old/midi.c
new file mode 100644
index 0000000000..1be951befb
--- /dev/null
+++ b/engines/sci/sfx/old/midi.c
@@ -0,0 +1,30 @@
+/***************************************************************************
+ midi.c Copyright (C) 2000 Rickard Lind
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+#ifndef _MIDI_MT32_H_
+#define _MIDI_MT32_H_
+
+int midi_mt32_open(guint8 *data_ptr, unsigned int data_length);
+int midi_mt32_close();
+
+int midi_mt32_noteoff(guint8 channel, guint8 note);
+int midi_mt32_noteon(guint8 channel, guint8 note, guint8 velocity);
+
+#endif /* _MIDI_MT32_H_ */
diff --git a/engines/sci/sfx/old/midi.h b/engines/sci/sfx/old/midi.h
new file mode 100644
index 0000000000..9a816781d3
--- /dev/null
+++ b/engines/sci/sfx/old/midi.h
@@ -0,0 +1,30 @@
+/***************************************************************************
+ midi.h Copyright (C) 2000 Rickard Lind
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+#ifndef _MIDI_H_
+#define _MIDI_H_
+
+int midi_open(guint8 *data_ptr, unsigned int data_length);
+int midi_close();
+
+int midi_noteoff(guint8 channel, guint8 note);
+int midi_noteon(guint8 channel, guint8 note, guint8 velocity);
+
+#endif /* _MIDI_H_ */
diff --git a/engines/sci/sfx/old/midi_mt32.c b/engines/sci/sfx/old/midi_mt32.c
new file mode 100644
index 0000000000..be5550bd93
--- /dev/null
+++ b/engines/sci/sfx/old/midi_mt32.c
@@ -0,0 +1,341 @@
+/***************************************************************************
+ midi_mt32.c Copyright (C) 2000 Rickard Lind
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+#include "glib.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include "midi_mt32.h"
+#include "midiout.h"
+
+#define RHYTHM_CHANNEL 9
+
+int midi_mt32_poke(guint32 address, guint8 *data, unsigned int n);
+int midi_mt32_poke_gather(guint32 address, guint8 *data1, unsigned int count1,
+ guint8 *data2, unsigned int count2);
+int midi_mt32_write_block(guint8 *data, unsigned int count);
+int midi_mt32_patch001_type(guint8 *data, unsigned int length);
+int midi_mt32_patch001_type0_length(guint8 *data, unsigned int length);
+int midi_mt32_patch001_type1_length(guint8 *data, unsigned int length);
+int midi_mt32_sysex_delay();
+
+static guint8 *data;
+static unsigned int length;
+static int type;
+static guint8 sysex_buffer[266] = {0xF0, 0x41, 0x10, 0x16, 0x12};
+
+static gint8 patch_map[128] = {
+ 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
+ 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
+ 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
+ 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
+ 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
+ 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
+ 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
+ 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127};
+static gint8 keyshift[128] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+static gint8 volume_adjust[128] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+static guint8 velocity_map_index[128] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+static guint8 velocity_map[4][128] = {
+ {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
+ 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
+ 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
+ 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
+ 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
+ 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
+ 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
+ 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127},
+ {0,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,
+ 39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,
+ 47,48,48,49,49,50,50,51,51,52,52,53,53,54,54,55,
+ 55,56,56,57,57,58,58,59,59,60,60,61,61,62,62,63,
+ 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
+ 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
+ 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
+ 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127},
+ {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
+ 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
+ 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
+ 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
+ 64,65,66,66,67,67,68,68,69,69,70,70,71,71,72,72,
+ 73,73,74,74,75,75,76,76,77,77,78,78,79,79,80,80,
+ 81,81,82,82,83,83,84,84,85,85,86,86,87,87,88,88,
+ 89,89,90,90,91,91,92,92,93,93,94,94,95,95,96,96},
+ {0,32,32,33,33,34,34,35,35,36,36,37,37,38,38,39,
+ 39,40,40,41,41,42,42,43,43,44,44,45,45,46,46,47,
+ 47,48,48,49,49,50,50,51,51,52,52,53,53,54,54,55,
+ 55,56,56,57,57,58,58,59,59,60,60,61,61,62,62,63,
+ 64,65,66,66,67,67,68,68,69,69,70,70,71,71,72,72,
+ 73,73,74,74,75,75,76,76,77,77,78,78,79,79,80,80,
+ 81,81,82,82,83,83,84,84,85,85,86,86,87,87,88,88,
+ 89,89,90,90,91,91,92,92,93,93,94,94,95,95,96,96}};
+static gint8 rhythmkey_map[128] = {
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,35,36,37,38,39,40,41,42,43,44,45,46,47,
+ 48,49,50,51,-1,-1,54,-1,56,-1,-1,-1,60,61,62,63,
+ 64,65,66,67,68,69,70,71,72,73,-1,75,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
+/* static struct {
+ gint8 sci_patch;
+ gint8 sci_volume;
+ gint8 sci_pitchwheel;
+ gint8 patch;
+ gint8 keyshift;
+ gint8 volume_adjust;
+ guint8 velocity_map_index;
+ guint8
+} channel[16]; */
+static guint8 master_volume;
+
+int midi_mt32_open(guint8 *data_ptr, unsigned int data_length)
+{
+ guint8 unknown_sysex[6] = {0x16, 0x16, 0x16, 0x16, 0x16, 0x16};
+ guint8 i, memtimbres;
+ unsigned int block2, block3;
+
+ if (midiout_open() < 0)
+ return -1;
+
+ data = data_ptr;
+ length = data_length;
+
+ type = midi_mt32_patch001_type(data, length);
+ printf("MT-32: Programming Roland MT-32 with patch.001 (v%i)\n", type);
+
+ if (type == 0) {
+ /* Display MT-32 initialization message */
+ printf("MT-32: Displaying Text: \"%.20s\"\n", data + 20);
+ midi_mt32_poke(0x200000, data + 20, 20);
+ midi_mt32_sysex_delay();
+
+ /* Write Patches (48 or 96) */
+ memtimbres = data[491];
+ block2 = (memtimbres * 246) + 492;
+ printf("MT-32: Writing Patches #01 - #32\n");
+ midi_mt32_poke(0x050000, data + 107, 256);
+ midi_mt32_sysex_delay();
+ if ((length > block2) &&
+ data[block2] == 0xAB &&
+ data[block2 + 1] == 0xCD) {
+ printf("MT-32: Writing Patches #33 - #64\n");
+ midi_mt32_poke_gather(0x050200, data + 363, 128, data + block2 + 2, 128);
+ midi_mt32_sysex_delay();
+ printf("MT-32: Writing Patches #65 - #96\n");
+ midi_mt32_poke(0x050400, data + block2 + 130, 256);
+ midi_mt32_sysex_delay();
+ block3 = block2 + 386;
+ } else {
+ printf("MT-32: Writing Patches #33 - #48\n");
+ midi_mt32_poke(0x050200, data + 363, 128);
+ midi_mt32_sysex_delay();
+ block3 = block2;
+ }
+ /* Write Memory Timbres */
+ for (i = 0; i < memtimbres; i++) {
+ printf("MT-32: Writing Memory Timbre #%02d: \"%.10s\"\n",
+ i + 1, data + 492 + i * 246);
+ midi_mt32_poke(0x080000 + (i << 9), data + 492 + i * 246, 246);
+ midi_mt32_sysex_delay();
+ }
+ /* Write Rhythm key map and Partial Reserve */
+ if ((length > block3) &&
+ data[block3] == 0xDC &&
+ data[block3 + 1] == 0xBA) {
+ printf("MT-32: Writing Rhythm key map\n");
+ midi_mt32_poke(0x030110, data + block3 + 2, 256);
+ midi_mt32_sysex_delay();
+ printf("MT-32: Writing Partial Reserve\n");
+ midi_mt32_poke(0x100004, data + block3 + 258, 9);
+ midi_mt32_sysex_delay();
+ }
+ /* Display MT-32 initialization done message */
+ printf("MT-32: Displaying Text: \"%.20s\"\n", data);
+ midi_mt32_poke(0x200000, data, 20);
+ midi_mt32_sysex_delay();
+ /* Write undocumented MT-32(?) SysEx */
+ printf("MT-32: Writing {F0 41 10 16 12 52 00 0A 16 16 16 16 16 16 20 F7}\n");
+ midi_mt32_poke(0x52000A, unknown_sysex, 6);
+ midi_mt32_sysex_delay();
+ return 0;
+ } else if (type == 1) {
+ midi_mt32_write_block(data + 1155, (data[1154] << 8) + data[1153]);
+ memcpy(patch_map, data, 128);
+ memcpy(keyshift, data + 128, 128);
+ memcpy(volume_adjust, data + 256, 128);
+ memcpy(velocity_map_index, data + 513, 128);
+ for (i = 0; i < 4; i++)
+ memcpy(velocity_map[i], data + 641 + i * 128, 128);
+ memcpy(rhythmkey_map, data + 384, 128);
+ return 0;
+ }
+ return -1;
+}
+
+int midi_mt32_close()
+{
+if (type == 0) {
+ printf("MT-32: Displaying Text: \"%.20s\"\n", data + 40);
+ midi_mt32_poke(0x200000, data + 40, 20);
+ midi_mt32_sysex_delay();
+}
+return midiout_close();
+}
+
+int midi_mt32_noteoff(guint8 channel, guint8 note)
+{
+ return 0;
+}
+
+int midi_mt32_noteon(guint8 channel, guint8 note, guint8 velocity)
+{
+/* guint8 buffer[3] = {0x90};
+ if (channel == RHYTHM_CHANNEL)
+ if (rhythmkey_map[note] == -1)
+ return 0;
+ else {
+ buffer[0] |= channel
+ buffer[1] = rhythmkey_map[note];
+ buffer[2] = velo
+ midi_write_event(buffer, 3);
+ }; */
+ return 0;
+}
+
+int midi_mt32_poke(guint32 address, guint8 *data, unsigned int count)
+{
+ guint8 checksum = 0;
+ unsigned int i;
+
+ if (count > 256) return -1;
+
+ checksum -= (sysex_buffer[5] = (char)((address >> 16) & 0x7F));
+ checksum -= (sysex_buffer[6] = (char)((address >> 8) & 0x7F));
+ checksum -= (sysex_buffer[7] = (char)(address & 0x7F));
+
+ for (i = 0; i < count; i++)
+ checksum -= (sysex_buffer[i + 8] = data[i]);
+
+ sysex_buffer[count + 8] = checksum & 0x7F;
+ sysex_buffer[count + 9] = 0xF7;
+
+ return midiout_write_block(sysex_buffer, count + 10);
+}
+
+int midi_mt32_poke_gather(guint32 address, guint8 *data1, unsigned int count1,
+ guint8 *data2, unsigned int count2)
+{
+ guint8 checksum = 0;
+ unsigned int i;
+
+ if ((count1 + count2) > 256) return -1;
+
+ checksum -= (sysex_buffer[5] = (char)((address >> 16) & 0x7F));
+ checksum -= (sysex_buffer[6] = (char)((address >> 8) & 0x7F));
+ checksum -= (sysex_buffer[7] = (char)(address & 0x7F));
+
+ for (i = 0; i < count1; i++)
+ checksum -= (sysex_buffer[i + 8] = data1[i]);
+ for (i = 0; i < count2; i++)
+ checksum -= (sysex_buffer[i + 8 + count1] = data2[i]);
+
+ sysex_buffer[count1 + count2 + 8] = checksum & 0x7F;
+ sysex_buffer[count1 + count2 + 9] = 0xF7;
+
+ return midiout_write_block(sysex_buffer, count1 + count2 + 10);
+}
+
+int midi_mt32_write_block(guint8 *data, unsigned int count)
+{
+ unsigned int block_start = 0;
+ unsigned int i = 0;
+
+ while (i < count) {
+ if ((data[i] == 0xF0) && (i != block_start)) {
+ midiout_write_block(data + block_start, i - block_start);
+ block_start = i;
+ }
+ if (data[i] == 0xF7) {
+ midiout_write_block(data + block_start, i - block_start + 1);
+ midi_mt32_sysex_delay();
+ block_start = i + 1;
+ }
+ i++;
+ }
+ if (count >= block_start)
+ midiout_write_block(data + block_start, count - block_start);
+ return 0;
+}
+
+int midi_mt32_patch001_type(guint8 *data, unsigned int length)
+{
+ /* length test */
+ if (length < 1155)
+ return 0;
+ if (length > 16889)
+ return 1;
+ if (midi_mt32_patch001_type0_length(data, length) &&
+ !midi_mt32_patch001_type1_length(data, length))
+ return 0;
+ if (midi_mt32_patch001_type1_length(data, length) &&
+ !midi_mt32_patch001_type0_length(data, length))
+ return 1;
+ return -1;
+}
+
+int midi_mt32_patch001_type0_length(guint8 *data, unsigned int length)
+{
+ unsigned int pos = 492 + 246 * data[491];
+
+ if ((length >= (pos + 386)) && (data[pos] == 0xAB) && (data[pos + 1] == 0xCD))
+ pos += 386;
+ if ((length >= (pos + 267)) && (data[pos] == 0xDC) && (data[pos + 1] == 0xBA))
+ pos += 267;
+ if (pos == length)
+ return 1;
+ return 0;
+}
+
+int midi_mt32_patch001_type1_length(guint8 *data, unsigned int length)
+{
+ if ((length >= 1155) && (((data[1154] << 8) + data[1153] + 1155) == length))
+ return 1;
+ return 0;
+}
+
+int midi_mt32_sysex_delay()
+{
+ usleep(320 * 63); /* One MIDI byte is 320us, 320us * 63 > 20ms */
+ return 0;
+}
diff --git a/engines/sci/sfx/old/midi_mt32.h b/engines/sci/sfx/old/midi_mt32.h
new file mode 100644
index 0000000000..0e14efd41d
--- /dev/null
+++ b/engines/sci/sfx/old/midi_mt32.h
@@ -0,0 +1,29 @@
+/***************************************************************************
+ midi_mt32.h Copyright (C) 2000 Rickard Lind
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+#ifndef _MIDI_MT32_H_
+#define _MIDI_MT32_H_
+
+#include "glib.h"
+
+int midi_mt32_open(guint8 *data_ptr, unsigned int data_length);
+int midi_mt32_close();
+
+#endif /* _MIDI_MT32_H_ */
diff --git a/engines/sci/sfx/old/midiout.c b/engines/sci/sfx/old/midiout.c
new file mode 100644
index 0000000000..262836baba
--- /dev/null
+++ b/engines/sci/sfx/old/midiout.c
@@ -0,0 +1,63 @@
+/***************************************************************************
+ midiout.c Copyright (C) 2000 Rickard Lind
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+#include "glib.h"
+#include "midiout.h"
+#include "midiout_alsaraw.h"
+#include "midiout_unixraw.h"
+
+static int (*midiout_ptr_close)();
+static int (*midiout_ptr_write)(guint8 *, unsigned int);
+
+static unsigned char running_status = 0;
+
+int midiout_open()
+{
+ midiout_ptr_close = midiout_alsaraw_close;
+ midiout_ptr_write = midiout_alsaraw_write;
+ return midiout_alsaraw_open(0, 0);
+
+ /*
+ midiout_ptr_close = midiout_unixraw_close;
+ midiout_ptr_write = midiout_unixraw_write;
+ return midiout_unixraw_open("/dev/midi00");
+ */
+}
+
+int midiout_close()
+{
+ return midiout_ptr_close();
+}
+
+int midiout_write_event(guint8 *buffer, unsigned int count)
+{
+ if (buffer[0] == running_status)
+ return midiout_ptr_write(buffer + 1, count - 1);
+ else {
+ running_status = buffer[0];
+ return midiout_ptr_write(buffer, count);
+ }
+}
+
+int midiout_write_block(guint8 *buffer, unsigned int count)
+{
+ running_status = 0;
+ return midiout_ptr_write(buffer, count);
+}
diff --git a/engines/sci/sfx/old/midiout.h b/engines/sci/sfx/old/midiout.h
new file mode 100644
index 0000000000..e0ce3915ae
--- /dev/null
+++ b/engines/sci/sfx/old/midiout.h
@@ -0,0 +1,29 @@
+/***************************************************************************
+ midiout.h Copyright (C) 2000 Rickard Lind
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+#ifndef _MIDIOUT_H_
+#define _MIDIOUT_H_
+
+int midiout_open();
+int midiout_close();
+int midiout_write_event(guint8 *buffer, unsigned int count);
+int midiout_write_block(guint8 *buffer, unsigned int count);
+
+#endif /* _MIDIOUT_H_ */
diff --git a/engines/sci/sfx/old/midiout_alsaraw.c b/engines/sci/sfx/old/midiout_alsaraw.c
new file mode 100644
index 0000000000..e95aa28425
--- /dev/null
+++ b/engines/sci/sfx/old/midiout_alsaraw.c
@@ -0,0 +1,51 @@
+/***************************************************************************
+ midiout_alsaraw.c Copyright (C) 2000 Rickard Lind
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+#include "glib.h"
+#include <stdio.h>
+#include <sys/asoundlib.h>
+#include "midiout_alsaraw.h"
+
+static snd_rawmidi_t *handle;
+
+int midiout_alsaraw_open(int card, int device)
+{
+ int err;
+
+ if ((err = snd_rawmidi_open(&handle, card, device, SND_RAWMIDI_OPEN_OUTPUT)) < 0) {
+ fprintf(stderr, "Open failed (%i): /dev/snd/midiC%iD%i\n", err, card, device);
+ return -1;
+ }
+ return 0;
+}
+
+int midiout_alsaraw_close()
+{
+ if (snd_rawmidi_close(handle) < 0)
+ return -1;
+ return 0;
+}
+
+int midiout_alsaraw_write(guint8 *buffer, unsigned int count)
+{
+ if (snd_rawmidi_write(handle, buffer, count) != count)
+ return -1;
+ return 0;
+}
diff --git a/engines/sci/sfx/old/midiout_alsaraw.h b/engines/sci/sfx/old/midiout_alsaraw.h
new file mode 100644
index 0000000000..cbf9da44c1
--- /dev/null
+++ b/engines/sci/sfx/old/midiout_alsaraw.h
@@ -0,0 +1,28 @@
+/***************************************************************************
+ midiout_alsaraw.h Copyright (C) 2000 Rickard Lind
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+#ifndef _MIDIOUT_ALSARAW_H_
+#define _MIDIOUT_ALSARAW_H_
+
+int midiout_alsaraw_open(int card, int device);
+int midiout_alsaraw_close();
+int midiout_alsaraw_write(guint8 *buffer, unsigned int count);
+
+#endif /* _MIDIOUT_ALSARAW_H_ */
diff --git a/engines/sci/sfx/old/midiout_unixraw.c b/engines/sci/sfx/old/midiout_unixraw.c
new file mode 100644
index 0000000000..a0dc85955f
--- /dev/null
+++ b/engines/sci/sfx/old/midiout_unixraw.c
@@ -0,0 +1,52 @@
+/***************************************************************************
+ midiout_unixraw.c Copyright (C) 2000 Rickard Lind
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+#include "glib.h"
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "midiout_unixraw.h"
+
+static int fd;
+
+int midiout_unixraw_open(char *devicename)
+{
+ if (!IS_VALID_FD(fd = open(devicename, O_WRONLY|O_SYNC))) {
+ fprintf(stderr, "Open failed (%i): %s\n", fd, devicename);
+ return -1;
+ }
+ return 0;
+}
+
+int midiout_unixraw_close()
+{
+ if (close(fd) < 0)
+ return -1;
+ return 0;
+}
+
+int midiout_unixraw_write(guint8 *buffer, unsigned int count)
+{
+ if (write(fd, buffer, count) != count)
+ return -1;
+ return 0;
+}
diff --git a/engines/sci/sfx/old/midiout_unixraw.h b/engines/sci/sfx/old/midiout_unixraw.h
new file mode 100644
index 0000000000..c5980696e9
--- /dev/null
+++ b/engines/sci/sfx/old/midiout_unixraw.h
@@ -0,0 +1,28 @@
+/***************************************************************************
+ midiout_unixraw.h Copyright (C) 2000 Rickard Lind
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+#ifndef _MIDIOUT_UNIXRAW_H_
+#define _MIDIOUT_UNIXRAW_H_
+
+int midiout_unixraw_open(char *devicename);
+int midiout_unixraw_close();
+int midiout_unixraw_write(guint8 *buffer, unsigned int count);
+
+#endif /* _MIDIOUT_UNIXRAW_H_ */
diff --git a/engines/sci/sfx/pcm-iterator.c b/engines/sci/sfx/pcm-iterator.c
new file mode 100644
index 0000000000..8d9b86ed0d
--- /dev/null
+++ b/engines/sci/sfx/pcm-iterator.c
@@ -0,0 +1,117 @@
+/***************************************************************************
+ pcm-iterator.c Copyright (C) 2002 Christoph Reichenbach
+
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public Licence as
+ published by the Free Software Foundaton; either version 2 of the
+ Licence, or (at your option) any later version.
+
+ It is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ merchantibility or fitness for a particular purpose. See the
+ GNU General Public Licence for more details.
+
+ You should have received a copy of the GNU General Public Licence
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+
+ Please contact the maintainer for any program-related bug reports or
+ inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+
+#include <sfx_iterator.h>
+#include <resource.h> /* for BREAKPOINT */
+#include <sci_memory.h>
+
+#define D ((pcm_data_internal_t *)self->internal)
+
+static int
+pi_poll(sfx_pcm_feed_t *self, byte *dest, int size);
+static void
+pi_destroy(sfx_pcm_feed_t *self);
+
+typedef struct {
+ byte *base_data;
+ byte *data;
+ int frames_left;
+} pcm_data_internal_t;
+
+
+static sfx_pcm_feed_t pcm_it_prototype = {
+ pi_poll,
+ pi_destroy,
+ NULL, /* No timestamp getter */
+ NULL, /* Internal data goes here */
+ {0,0,0}, /* Must fill in configuration */
+ "song-iterator",
+ 0, /* Ideally the resource number should go here */
+ 0 /* The mixer computes this for us */
+};
+
+
+sfx_pcm_feed_t *
+sfx_iterator_make_feed(byte *base_data,
+ int offset,
+ int size,
+ sfx_pcm_config_t conf)
+{
+ sfx_pcm_feed_t *feed;
+ pcm_data_internal_t *idat;
+ byte *data = base_data + offset;
+
+ if (!data) {
+ /* Now this is silly; why'd you call this function in the first place? */
+ return NULL;
+ }
+ sci_refcount_incref(base_data);
+
+ idat = (pcm_data_internal_t*)sci_malloc(sizeof(pcm_data_internal_t));
+ idat->base_data = base_data;
+ idat->data = data;
+ idat->frames_left = size;
+ feed = (sfx_pcm_feed_t*)sci_malloc(sizeof(sfx_pcm_feed_t));
+ *feed = pcm_it_prototype;
+ feed->internal = idat;
+ feed->conf = conf;
+
+ return feed;
+}
+
+
+static int
+pi_poll(sfx_pcm_feed_t *self, byte *dest, int size)
+{
+ int data_len;
+
+ if (size >= D->frames_left)
+ size = D->frames_left;
+
+ D->frames_left -= size;
+
+ data_len = size * self->frame_size;
+
+ memcpy(dest, D->data, data_len);
+#if 0
+memset(dest, 0xff, data_len);
+#endif
+
+ D->data += data_len;
+
+ return size;
+}
+
+static void
+pi_destroy(sfx_pcm_feed_t *self)
+{
+ sci_refcount_decref(D->base_data);
+ sci_free(D);
+ sci_free(self);
+}
diff --git a/engines/sci/sfx/pcm_device/Makefile.am b/engines/sci/sfx/pcm_device/Makefile.am
new file mode 100644
index 0000000000..b13a3aab0b
--- /dev/null
+++ b/engines/sci/sfx/pcm_device/Makefile.am
@@ -0,0 +1,7 @@
+noinst_LIBRARIES = libscipcm.a
+INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@
+AM_CFLAGS = $(SDL_CFLAGS)
+EXTRA_DIST = audiobuf.h
+libscipcm_a_SOURCES = audiobuf.c pcm_devices.c sdl.c alsa.c
+audbuf_test_LDADD = libscipcm.a ../../scicore/libscicore.a
+check_PROGRAMS = audbuf_test
diff --git a/engines/sci/sfx/pcm_device/alsa.c b/engines/sci/sfx/pcm_device/alsa.c
new file mode 100644
index 0000000000..26d4c35044
--- /dev/null
+++ b/engines/sci/sfx/pcm_device/alsa.c
@@ -0,0 +1,387 @@
+/***************************************************************************
+ alsa.c Copyright (C) 2004 Walter van Niftrik
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Walter van Niftrik <w.f.b.w.v.niftrik@stud.tue.nl>
+
+***************************************************************************/
+
+/* Based on ALSA's pcm.c example. */
+
+#include <sfx_pcm.h>
+#include "audiobuf.h"
+
+#ifdef HAVE_ALSA
+#ifdef HAVE_PTHREAD
+
+#define ALSA_PCM_NEW_HW_PARAMS_API
+#define ALSA_PCM_NEW_SW_PARAMS_API
+
+#include <alsa/asoundlib.h>
+#include <pthread.h>
+
+static const char *device = "default"; /* FIXME */
+static snd_pcm_format_t format = SND_PCM_FORMAT_S16;
+static unsigned int rate = 44100; /* FIXME */
+static unsigned int channels = 2; /* FIXME */
+static unsigned int buffer_time = 100000; /* FIXME */
+static unsigned int period_time = 1000000/60; /* 60Hz */ /* FIXME */
+
+static snd_pcm_sframes_t buffer_size;
+static snd_pcm_sframes_t period_size;
+
+static int frame_size;
+static long last_callback_secs, last_callback_usecs;
+
+static sfx_audio_buf_t audio_buffer;
+static void (*alsa_sfx_timer_callback)(void *data);
+static void *alsa_sfx_timer_data;
+
+static snd_pcm_t *handle;
+
+static pthread_t thread;
+static volatile byte run_thread;
+
+static pthread_mutex_t mutex;
+
+static int
+xrun_recovery(snd_pcm_t *handle, int err)
+{
+ if (err == -EPIPE) { /* under-run */
+ err = snd_pcm_prepare(handle);
+ if (err < 0)
+ fprintf(stderr, "Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
+ return 0;
+ } else if (err == -ESTRPIPE) {
+ while ((err = snd_pcm_resume(handle)) == -EAGAIN)
+ sleep(1); /* wait until the suspend flag is released */
+ if (err < 0) {
+ err = snd_pcm_prepare(handle);
+ if (err < 0)
+ fprintf(stderr, "Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err));
+ }
+ return 0;
+ }
+ return err;
+}
+
+static void *
+alsa_thread(void *arg)
+{
+ gint16 *ptr;
+ int err, cptr;
+ guint8 *buf;
+ sfx_pcm_device_t *self = (sfx_pcm_device_t *) arg;
+
+ buf = (guint8 *) malloc(period_size * frame_size);
+
+ while (run_thread) {
+ ptr = (gint16 *) buf;
+ cptr = period_size;
+
+ sci_gettime(&last_callback_secs, &last_callback_usecs);
+
+ self->timer->block();
+
+ if (alsa_sfx_timer_callback)
+ alsa_sfx_timer_callback(alsa_sfx_timer_data);
+
+ self->timer->unblock();
+
+ sfx_audbuf_read(&audio_buffer, buf, period_size);
+
+ while (cptr > 0) {
+ err = snd_pcm_writei(handle, ptr, cptr);
+ if (err == -EAGAIN)
+ continue;
+ if (err < 0) {
+ if (xrun_recovery(handle, err) < 0) {
+ fprintf(stderr, "[SND:ALSA] Write error: %s\n", snd_strerror(err));
+ run_thread = 0;
+ }
+ break; /* skip one period */
+ }
+ ptr += err * channels;
+ cptr -= err;
+ }
+ }
+
+ free(buf);
+ return NULL;
+}
+
+static sfx_timestamp_t
+pcmout_alsa_output_timestamp(sfx_pcm_device_t *self)
+{
+ /* Number of frames enqueued in the output device: */
+ int delta = (buffer_size - period_size) / frame_size
+ /* Number of frames enqueued in the internal audio buffer: */
+ + audio_buffer.frames_nr;
+
+ return sfx_timestamp_add(sfx_new_timestamp(last_callback_secs,
+ last_callback_usecs,
+ rate),
+ delta);
+}
+
+static int
+pcmout_alsa_init(sfx_pcm_device_t *self)
+{
+ unsigned int rrate;
+ int err, dir;
+ snd_pcm_hw_params_t *hwparams;
+ snd_pcm_sw_params_t *swparams;
+ pthread_attr_t attr;
+
+ snd_pcm_hw_params_alloca(&hwparams);
+ snd_pcm_sw_params_alloca(&swparams);
+
+ err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0);
+ if (err < 0) {
+ sciprintf("[SND:ALSA] Playback open error: %s\n", snd_strerror(err));
+ return SFX_ERROR;
+ }
+ err = snd_pcm_hw_params_any(handle, hwparams);
+ if (err < 0) {
+ sciprintf("[SND:ALSA] Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
+ return SFX_ERROR;
+ }
+ err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
+ if (err < 0) {
+ sciprintf("[SND:ALSA] Access type not available for playback: %s\n", snd_strerror(err));
+ return SFX_ERROR;
+ }
+ err = snd_pcm_hw_params_set_format(handle, hwparams, format);
+ if (err < 0) {
+ sciprintf("[SND:ALSA] Sample format not available for playback: %s\n", snd_strerror(err));
+ return SFX_ERROR;
+ }
+ err = snd_pcm_hw_params_set_channels(handle, hwparams, channels);
+ if (err < 0) {
+ sciprintf("[SND:ALSA] Channels count (%i) not available for playback: %s\n", channels, snd_strerror(err));
+ return SFX_ERROR;
+ }
+ rrate = rate;
+ err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &rrate, 0);
+ if (err < 0) {
+ sciprintf("[SND:ALSA] Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
+ return SFX_ERROR;
+ }
+ if (rrate != rate) {
+ sciprintf("[SND:ALSA] Rate doesn't match (requested %iHz, get %iHz)\n", rate, err);
+ return SFX_ERROR;
+ }
+ err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir);
+ if (err < 0) {
+ sciprintf("[SND:ALSA] Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err));
+ return SFX_ERROR;
+ }
+ err = snd_pcm_hw_params_get_buffer_size(hwparams, (snd_pcm_uframes_t*)&buffer_size);
+ if (err < 0) {
+ sciprintf("[SND:ALSA] Unable to get buffer size for playback: %s\n", snd_strerror(err));
+ return SFX_ERROR;
+ }
+ err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir);
+ if (err < 0) {
+ sciprintf("[SND:ALSA] Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err));
+ return SFX_ERROR;
+ }
+ err = snd_pcm_hw_params_get_period_size(hwparams, (snd_pcm_uframes_t*)&period_size, &dir);
+ if (err < 0) {
+ sciprintf("[SND:ALSA] Unable to get period size for playback: %s\n", snd_strerror(err));
+ return SFX_ERROR;
+ }
+ if (period_size >= buffer_size) {
+ sciprintf("[SND:ALSA] Period size %i matches or exceeds buffer size %i\n", period_size, buffer_size);
+ return SFX_ERROR;
+ }
+ err = snd_pcm_hw_params(handle, hwparams);
+ if (err < 0) {
+ sciprintf("[SND:ALSA] Unable to set hw params for playback: %s\n", snd_strerror(err));
+ return SFX_ERROR;
+ }
+ err = snd_pcm_sw_params_current(handle, swparams);
+ if (err < 0) {
+ sciprintf("[SND:ALSA] Unable to determine current swparams for playback: %s\n", snd_strerror(err));
+ return SFX_ERROR;
+ }
+ err = snd_pcm_sw_params_set_start_threshold(handle, swparams, buffer_size);
+ if (err < 0) {
+ sciprintf("[SND:ALSA] Unable to set start threshold mode for playback: %s\n", snd_strerror(err));
+ return SFX_ERROR;
+ }
+ err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size);
+ if (err < 0) {
+ sciprintf("[SND:ALSA] Unable to set avail min for playback: %s\n", snd_strerror(err));
+ return SFX_ERROR;
+ }
+ err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1);
+ if (err < 0) {
+ sciprintf("[SND:ALSA] Unable to set transfer align for playback: %s\n", snd_strerror(err));
+ return SFX_ERROR;
+ }
+ err = snd_pcm_sw_params(handle, swparams);
+ if (err < 0) {
+ sciprintf("[SND:ALSA] Unable to set sw params for playback: %s\n", snd_strerror(err));
+ return SFX_ERROR;
+ }
+
+ self->buf_size = buffer_size;
+ self->conf.rate = rate;
+ self->conf.stereo = channels > 1;
+ self->conf.format = SFX_PCM_FORMAT_S16_NATIVE;
+
+ frame_size = SFX_PCM_FRAME_SIZE(self->conf);
+
+ sfx_audbuf_init(&audio_buffer, self->conf);
+
+ if (pthread_mutex_init(&mutex, NULL) != 0) {
+ sciprintf("[SND:ALSA] Failed to create mutex\n");
+ return SFX_ERROR;
+ }
+
+ run_thread = 1;
+ if (pthread_create(&thread, NULL, alsa_thread, self) != 0) {
+ sciprintf("[SND:ALSA] Failed to create thread\n");
+ return SFX_ERROR;
+ }
+
+ return SFX_OK;
+}
+
+static int
+pcmout_alsa_output(sfx_pcm_device_t *self, byte *buf,
+ int count, sfx_timestamp_t *ts)
+{
+ if (ts)
+ sfx_audbuf_write_timestamp(&audio_buffer, *ts);
+
+ sfx_audbuf_write(&audio_buffer, buf, count);
+ return SFX_OK;
+}
+
+static int
+pcmout_alsa_set_option(sfx_pcm_device_t *self, char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+static void
+pcmout_alsa_exit(sfx_pcm_device_t *self)
+{
+ int err;
+
+ run_thread = 0;
+ sciprintf("[SND:ALSA] Waiting for PCM thread to exit... ");
+ if (!pthread_join(thread, NULL))
+ sciprintf("OK\n");
+ else
+ sciprintf("Failed\n");
+
+ pthread_mutex_destroy(&mutex);
+
+ if ((err = snd_pcm_drop(handle)) < 0) {
+ sciprintf("[SND:ALSA] Can't stop PCM device: %s\n", snd_strerror(err));
+ }
+ if ((err = snd_pcm_close(handle)) < 0) {
+ sciprintf("[SND:ALSA] Can't close PCM device: %s\n", snd_strerror(err));
+ }
+
+ sfx_audbuf_free(&audio_buffer);
+}
+
+static int
+timer_alsa_set_option(char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+
+static int
+timer_alsa_init(void (*callback)(void *data), void *data)
+{
+ alsa_sfx_timer_callback = callback;
+ alsa_sfx_timer_data = data;
+
+ return SFX_OK;
+}
+
+static int
+timer_alsa_stop(void)
+{
+ alsa_sfx_timer_callback = NULL;
+
+ return SFX_OK;
+}
+
+static int
+timer_alsa_block(void)
+{
+ if (pthread_mutex_lock(&mutex) != 0) {
+ fprintf(stderr, "[SND:ALSA] Failed to lock mutex\n");
+ return SFX_ERROR;
+ }
+
+ return SFX_OK;
+}
+
+static int
+timer_alsa_unblock(void)
+{
+ if (pthread_mutex_unlock(&mutex) != 0) {
+ fprintf(stderr, "[SND:ALSA] Failed to unlock mutex\n");
+ return SFX_ERROR;
+ }
+
+ return SFX_OK;
+}
+
+#define ALSA_PCM_VERSION "0.2"
+
+sfx_timer_t pcmout_alsa_timer = {
+ "alsa-pcm-timer",
+ ALSA_PCM_VERSION,
+ 0,
+ 0,
+ timer_alsa_set_option,
+ timer_alsa_init,
+ timer_alsa_stop,
+ timer_alsa_block,
+ timer_alsa_unblock
+};
+
+sfx_pcm_device_t sfx_pcm_driver_alsa = {
+ "alsa",
+ ALSA_PCM_VERSION,
+ pcmout_alsa_init,
+ pcmout_alsa_exit,
+ pcmout_alsa_set_option,
+ pcmout_alsa_output,
+ pcmout_alsa_output_timestamp,
+ {0, 0, 0},
+ 0,
+ &pcmout_alsa_timer,
+ NULL
+};
+
+#endif /* HAVE_PTHREAD */
+#endif /* HAVE_ALSA */
diff --git a/engines/sci/sfx/pcm_device/audbuf_test.c b/engines/sci/sfx/pcm_device/audbuf_test.c
new file mode 100644
index 0000000000..216aa9079e
--- /dev/null
+++ b/engines/sci/sfx/pcm_device/audbuf_test.c
@@ -0,0 +1,190 @@
+/***************************************************************************
+ audbuf_test.c Copyright (C) 2002 Christoph Reichenbach
+
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public Licence as
+ published by the Free Software Foundaton; either version 2 of the
+ Licence, or (at your option) any later version.
+
+ It is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ merchantibility or fitness for a particular purpose. See the
+ GNU General Public Licence for more details.
+
+ You should have received a copy of the GNU General Public Licence
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+
+ Please contact the maintainer for any program-related bug reports or
+ inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+
+#include "audiobuf.h"
+#if 0
+sfx_audio_buf_t buf;
+
+#define MIN_FRAMESIZE 1
+#define MAX_FRAMESIZE 8
+
+
+void
+tester_write(unsigned char *data, int datalen, int framesize, int gran)
+{
+ int i;
+
+ for (i = 0; i < datalen; i += gran) {
+ int size = (i + gran < datalen)? gran : datalen - i;
+
+ sfx_audbuf_write(&buf, data + (i * framesize), framesize, size);
+ }
+}
+
+
+void
+tester_read(unsigned char *data, int datalen, int framesize, int gran)
+{
+ unsigned char *readdata = malloc(datalen * framesize);
+ int i;
+
+ for (i = 0; i < datalen; i += gran) {
+ int size = (i + gran < datalen)? gran : datalen - i;
+ int j;
+
+ sfx_audbuf_read(&buf, readdata + (i * framesize), framesize, size);
+ for (j = 0; j < gran * framesize; j++) {
+ int offset = i*framesize + j;
+
+ if (data[i] != readdata[i]) {
+ fprintf(stderr, "[ERROR] Mismatch at offset %08x (sample #%d): Expected %02x, got %02x\n",
+ offset, i, readdata[i], data[i]);
+ }
+ }
+ }
+
+ free(readdata);
+}
+
+
+void
+test1(unsigned char *data, int len)
+ /* Test the 'regular' case */
+{
+ int framesize;
+ int stepsize;
+
+ fprintf(stderr, "[Test-1] Commenced; len=%d.\n", len);
+
+ for (framesize = MAX_FRAMESIZE; framesize >= MIN_FRAMESIZE; framesize >>= 1) {
+ fprintf(stderr, "[Test-1] Writing frame size %d\n", framesize);
+ for (stepsize = 1; stepsize <= len; stepsize++)
+ tester_write(data, len / framesize, framesize, stepsize);
+ }
+
+ for (framesize = MAX_FRAMESIZE; framesize >= MIN_FRAMESIZE; framesize >>= 1) {
+ fprintf(stderr, "[Test-1] Reading frame size %d\n", framesize);
+ for (stepsize = len; stepsize >= 1; stepsize--)
+ tester_read(data, len / framesize, framesize, stepsize);
+ }
+
+ fprintf(stderr, "[Test-1] Completed.\n");
+}
+
+#define TEST2_COUNT 10
+#define TEST2_LEN 3
+
+void
+test2(unsigned char *data, int framesize)
+ /* Test whether buffer underrun repeats are handled correctly */
+{
+ int i;
+ unsigned char *src;
+
+ fprintf(stderr, "[Test-2] Commenced; framesize=%d.\n", framesize);
+
+ sfx_audbuf_write(&buf, data, framesize, 1);
+ src = malloc(framesize * TEST2_LEN);
+
+ for (i = 0; i < TEST2_COUNT + 1; i++) {
+ int inst;
+ sfx_audbuf_read(&buf, src, framesize, TEST2_LEN);
+
+ for (inst = 0; inst < TEST2_LEN; inst++) {
+ int offset = inst * framesize;
+ int j;
+
+ for (j = 0; j < framesize; j++)
+ if (src[j + offset] != data[j]) {
+ fprintf(stderr, "[ERROR] At copy sample %d, frame %d, offset %d: Expected %02x, got %02x\n",
+ i, inst, j, data[j], src[j+offset]);
+ }
+ }
+ memset(src, 0xbf, framesize * TEST2_LEN);
+ }
+
+ free(src);
+ fprintf(stderr, "[Test-1] Completed.\n");
+}
+
+
+#define CHUNKS_NR 4
+
+#define CHUNK_LEN_0 8
+#define CHUNK_LEN_1 20
+#define CHUNK_LEN_2 16
+#define CHUNK_LEN_3 40
+
+unsigned char test_data_0[CHUNK_LEN_0] =
+ { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+unsigned char test_data_1[CHUNK_LEN_1] =
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff,
+ 0xff, 0x00, 0xff, 0x00};
+unsigned char test_data_2[CHUNK_LEN_2] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+unsigned char test_data_3[CHUNK_LEN_3] =
+ { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+ 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88 };
+
+struct {
+ int len;
+ unsigned char *data;
+} test_chunks[CHUNKS_NR] = {
+ { CHUNK_LEN_0, test_data_0 },
+ { CHUNK_LEN_1, test_data_1 },
+ { CHUNK_LEN_2, test_data_2 },
+ { CHUNK_LEN_3, test_data_3 }
+};
+
+int
+main(int argc, char **argv)
+{
+ int i;
+
+ sfx_audbuf_init(&buf);
+ for (i = 0; i < CHUNKS_NR; i++) {
+ int k;
+
+ /* for (k = MAX_FRAMESIZE; k >= MIN_FRAMESIZE; k >>= 1)
+ test2(test_chunks[i].data, k);*/
+
+ test1(test_chunks[i].data, test_chunks[i].len);
+ }
+ sfx_audbuf_exit(&buf);
+
+ return 0;
+}
+#else
+int main() {}
+#endif
diff --git a/engines/sci/sfx/pcm_device/audiobuf.c b/engines/sci/sfx/pcm_device/audiobuf.c
new file mode 100644
index 0000000000..fa23708ce4
--- /dev/null
+++ b/engines/sci/sfx/pcm_device/audiobuf.c
@@ -0,0 +1,348 @@
+/***************************************************************************
+ audiobuf.c Copyright (C) 2003 Christoph Reichenbach
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+
+#include "audiobuf.h"
+
+#define NO_BUFFER_UNDERRUN 0
+#define SAW_BUFFER_UNDERRUN 1
+#define REPORTED_BUFFER_UNDERRUN 2
+
+static int
+buffer_underrun_status = NO_BUFFER_UNDERRUN;
+
+
+static sfx_audio_buf_chunk_t *
+sfx_audbuf_alloc_chunk(void)
+{
+ sfx_audio_buf_chunk_t *ch = (sfx_audio_buf_chunk_t*)sci_malloc(sizeof(sfx_audio_buf_chunk_t));
+ ch->used = 0;
+ ch->next = NULL;
+ ch->prev = NULL;
+
+ return ch;
+}
+
+void
+sfx_audbuf_init(sfx_audio_buf_t *buf, sfx_pcm_config_t pcm_conf)
+{
+ int framesize = SFX_PCM_FRAME_SIZE(pcm_conf);
+ byte silence[16];
+ int silencew = pcm_conf.format & ~SFX_PCM_FORMAT_LMASK;
+
+ /* Determine the correct 'silence' for the channel and install it */
+ /* Conservatively assume stereo */
+ if (pcm_conf.format & SFX_PCM_FORMAT_16) {
+ if (pcm_conf.format & SFX_PCM_FORMAT_LE) {
+ silence[0] = silencew & 0xff;
+ silence[1] = (silencew >> 8) & 0xff;
+ } else {
+ silence[0] = (silencew >> 8) & 0xff;
+ silence[1] = silencew & 0xff;
+ }
+ memcpy(silence + 2, silence, 2);
+ } else {
+ silence[0] = silencew;
+ silence[1] = silencew;
+ }
+
+ buf->last = buf->first = sfx_audbuf_alloc_chunk();
+ buf->unused = NULL;
+ memcpy(buf->last_frame, silence, framesize); /* Initialise, in case we
+ ** underrun before the
+ ** first write */
+ buf->read_offset = 0;
+ buf->framesize = framesize;
+ buf->read_timestamp.secs = -1; /* Mark as inactive */
+ buf->frames_nr = 0;
+}
+
+static void
+sfx_audbuf_free_chain(sfx_audio_buf_chunk_t *b)
+{
+ while (b) {
+ sfx_audio_buf_chunk_t *n = b->next;
+ sci_free(b);
+ b = n;
+ }
+}
+
+void
+sfx_audbuf_free(sfx_audio_buf_t *buf)
+{
+ sfx_audbuf_free_chain(buf->first);
+ sfx_audbuf_free_chain(buf->unused);
+ buf->first = buf->last = buf->unused = NULL;
+ buf->read_offset = (int) 0xdeadbeef;
+}
+
+void
+sfx_audbuf_write(sfx_audio_buf_t *buf, unsigned char *src, int frames)
+{
+ /* In here, we compute PER BYTE */
+ int data_left = buf->framesize * frames;
+
+ if (!buf->last) {
+ fprintf(stderr, "FATAL: Violation of audiobuf.h usage protocol: Must use 'init' before 'write'\n");
+ exit(1);
+ }
+
+ if (buffer_underrun_status == SAW_BUFFER_UNDERRUN) {
+ /* Print here to avoid threadsafeness issues */
+ sciprintf("[audiobuf] Buffer underrun\n");
+ buffer_underrun_status = REPORTED_BUFFER_UNDERRUN;
+ }
+
+ buf->frames_nr += frames;
+
+ while (data_left) {
+ int cpsize;
+ int buf_free;
+ buf_free = SFX_AUDIO_BUF_SIZE - buf->last->used;
+
+
+ if (buf_free >= data_left)
+ cpsize = data_left;
+ else
+ cpsize = buf_free;
+
+ /* Copy and advance pointers */
+ memcpy(buf->last->data + buf->last->used, src, cpsize);
+ data_left -= cpsize;
+ buf->last->used += cpsize;
+ src += cpsize;
+
+ if (buf->last->used == SFX_AUDIO_BUF_SIZE) {
+ if (!buf->last->next) {
+ sfx_audio_buf_chunk_t *old = buf->last;
+ if (buf->unused) { /* Re-use old chunks */
+ sfx_audio_buf_chunk_t *buf_next_unused = buf->unused->next;
+ buf->unused->next = NULL;
+ buf->unused->used = 0;
+
+ buf->last->next = buf->unused;
+ buf->unused = buf_next_unused;
+ } else /* Allocate */
+ buf->last->next =
+ sfx_audbuf_alloc_chunk();
+
+ buf->last->prev = old;
+ }
+
+ buf->last = buf->last->next;
+ }
+ }
+
+#ifdef TRACE_BUFFER
+ {
+ sfx_audio_buf_chunk_t *c = buf->first;
+ int t = buf->read_offset;
+
+ while (c) {
+ fprintf(stderr, "-> [");
+ for (; t < c->used; t++)
+ fprintf(stderr, " %02x", c->data[t]);
+ t = 0;
+ fprintf(stderr, " ] ");
+ c = c->next;
+ }
+ fprintf(stderr, "\n");
+ }
+#endif
+
+ if (frames && (src - buf->framesize) != buf->last_frame)
+ /* Backup last frame, unless we're already filling from it */
+ memcpy(buf->last_frame, src - buf->framesize, buf->framesize);
+}
+
+int
+sfx_audbuf_read(sfx_audio_buf_t *buf, unsigned char *dest, int frames)
+{
+ int written = 0;
+
+ if (frames <= 0)
+ return 0;
+
+ if (buf->read_timestamp.secs >= 0) {
+ /* Have a timestamp? Must update it! */
+ buf->read_timestamp =
+ sfx_timestamp_add(buf->read_timestamp,
+ frames);
+
+ }
+
+ buf->frames_nr -= frames;
+ if (buf->frames_nr < 0)
+ buf->frames_nr = 0;
+
+#ifdef TRACE_BUFFER
+ {
+ sfx_audio_buf_chunk_t *c = buf->first;
+ int t = buf->read_offset;
+
+ while (c) {
+ fprintf(stderr, "-> [");
+ for (; t < c->used; t++)
+ fprintf(stderr, " %02x", c->data[t]);
+ t = 0;
+ fprintf(stderr, " ] ");
+ c = c->next;
+ }
+ fprintf(stderr, "\n");
+ }
+#endif
+
+ while (frames) {
+ int data_needed = frames * buf->framesize;
+ int rdbytes = data_needed;
+ int rdframes;
+
+ if (rdbytes > buf->first->used - buf->read_offset)
+ rdbytes = buf->first->used - buf->read_offset;
+
+ memcpy(dest, buf->first->data + buf->read_offset, rdbytes);
+
+ buf->read_offset += rdbytes;
+ dest += rdbytes;
+
+ if (buf->read_offset == SFX_AUDIO_BUF_SIZE) {
+ /* Continue to next, enqueue the current chunk as
+ ** being unused */
+ sfx_audio_buf_chunk_t *lastfirst = buf->first;
+
+ buf->first = buf->first->next;
+ lastfirst->next = buf->unused;
+ buf->unused = lastfirst;
+
+ buf->read_offset = 0;
+ }
+
+ rdframes = (rdbytes / buf->framesize);
+ frames -= rdframes;
+ written += rdframes;
+
+ if (frames &&
+ (!buf->first || buf->read_offset == buf->first->used)) {
+ fprintf(stderr, "Underrun by %d frames at %d\n", frames,
+ buf->read_timestamp.frame_rate);
+ /* Buffer underrun! */
+ if (!buffer_underrun_status == NO_BUFFER_UNDERRUN) {
+ buffer_underrun_status = SAW_BUFFER_UNDERRUN;
+ }
+ do {
+ memcpy(dest, buf->last_frame, buf->framesize);
+ dest += buf->framesize;
+ } while (--frames);
+ }
+
+ }
+
+ return written;
+}
+
+#if 0
+static void
+_sfx_audbuf_rewind_stream(sfx_audio_buf_t *buf, int delta)
+{
+ if (delta > buf->frames_nr)
+ delta = buf->frames_nr;
+
+
+ fprintf(stderr, "Rewinding %d\n", delta);
+ buf->frames_nr -= delta;
+
+ /* From here on, 'delta' means the number of BYTES to remove */
+ delta *= buf->framesize;
+
+ while (delta) {
+ if (buf->last->used >= delta) {
+ fprintf(stderr, "Subtracting from %d %d\n", buf->last->used, delta);
+ buf->last->used -= delta;
+ delta = 0;
+ } else {
+ fprintf(stderr, "Must do block-unuse\n");
+ delta -= buf->last->used;
+ buf->last->used = 0;
+ buf->last->next = buf->unused;
+ buf->unused = buf->last;
+ buf->last = buf->unused->prev;
+ buf->unused->prev = NULL;
+ }
+ }
+}
+#endif
+
+void
+sfx_audbuf_write_timestamp(sfx_audio_buf_t *buf, sfx_timestamp_t ts)
+{
+ sfx_timestamp_t newstamp;
+
+ newstamp = sfx_timestamp_add(ts, -buf->frames_nr);
+
+
+ if (buf->read_timestamp.secs <= 0)
+ /* Initial stamp */
+ buf->read_timestamp = newstamp;
+ else {
+ int delta = sfx_timestamp_frame_diff(newstamp, buf->read_timestamp);
+ long s1,s2,s3,u1,u2,u3;
+ sfx_timestamp_gettime(&(buf->read_timestamp), &s1, &u1);
+ sfx_timestamp_gettime(&(newstamp), &s2, &u2);
+ sfx_timestamp_gettime(&(ts), &s3, &u3);
+
+ if (delta < 0) {
+#if 0
+ /* fprintf(stderr, "[SFX-BUF] audiobuf.c: Timestamp delta %d at %d: Must rewind (not implemented yet)\n",
+ delta, buf->read_timestamp.frame_rate);*/
+ _sfx_audbuf_rewind_stream(buf, -delta);
+ buf->read_timestamp = newstamp;
+#endif
+ } else if (delta > 0) {
+ fprintf(stderr, "[SFX-BUF] audiobuf.c: Timestamp delta %d at %d: Filling in as silence frames\n",
+ delta, buf->read_timestamp.frame_rate);
+ /* Fill up with silence */
+ while (delta--) {
+ sfx_audbuf_write(buf, buf->last_frame, 1);
+ }
+ buf->read_timestamp = newstamp;
+ }
+ }
+}
+
+int
+sfx_audbuf_read_timestamp(sfx_audio_buf_t *buf, sfx_timestamp_t *ts)
+{
+ if (buf->read_timestamp.secs > 0) {
+ *ts = buf->read_timestamp;
+ return 0;
+ } else {
+ ts->secs = -1;
+ ts->usecs = -1;
+ ts->frame_offset = -1;
+ ts->frame_rate = -1;
+ return 1; /* No timestamp */
+ }
+}
diff --git a/engines/sci/sfx/pcm_device/audiobuf.h b/engines/sci/sfx/pcm_device/audiobuf.h
new file mode 100644
index 0000000000..b2019aec17
--- /dev/null
+++ b/engines/sci/sfx/pcm_device/audiobuf.h
@@ -0,0 +1,140 @@
+/***************************************************************************
+ audiobuf.h Copyright (C) 2003 Christoph Reichenbach
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+
+/* Auxiliary audio buffer for PCM devices
+** Polled PCM devices must store data written to them until it is explicitly
+** requiested. This is facilitated by the structures and functions defined
+** here.
+** This is generic for all PCM devices; it implies no specific requirements.
+**
+** Usage: Introduce an sfx_audio_buf_t into your state and make sure to use
+** each of the functions provided here at least once in the appropriate
+** places.
+*/
+
+
+#ifndef _AUDIOBUF_H_
+#define _AUDIOBUF_H_
+
+#include <resource.h>
+#include <sfx_time.h>
+#include <sci_memory.h>
+#include <sfx_pcm.h>
+
+
+#define SFX_AUDIO_BUF_SIZE 8192 /* Must be multiple of framesize */
+#define SFX_AUDIO_MAX_FRAME 8 /* Max. individual frame size */
+
+typedef struct _sfx_audio_buf_chunk {
+ unsigned char data[SFX_AUDIO_BUF_SIZE];
+ int used;
+ struct _sfx_audio_buf_chunk *prev;
+ struct _sfx_audio_buf_chunk *next;
+} sfx_audio_buf_chunk_t;
+
+typedef struct {
+ int read_offset;
+ sfx_audio_buf_chunk_t *first; /* Next to read-- can be = last */
+ sfx_audio_buf_chunk_t *last; /* Next to write-- can be = first */
+ sfx_audio_buf_chunk_t *unused; /* Unused chunk list, can be NULL */
+ unsigned char last_frame[SFX_AUDIO_MAX_FRAME];
+ /* Contains the last frame successfully read; used for buffer
+ ** underruns to avoid crack before silance */
+ sfx_timestamp_t read_timestamp; /* Timestamp for reading */
+ int frames_nr; /* Total number of frames currently in between reading and writing */
+ int framesize;
+} sfx_audio_buf_t;
+
+
+void
+sfx_audbuf_init(sfx_audio_buf_t *buf, sfx_pcm_config_t conf);
+/* Initialises an audio buffer
+** Parameters: (sfx_audio_buf_t *) buf: The buffer to initialise
+** (sfx_pcm_config_t) conf: The configuration for which the buffer should
+** be set up
+** Modifies : (sfx_audio_buf_t) *buf
+*/
+
+void
+sfx_audbuf_free(sfx_audio_buf_t *buf);
+/* Frees all memory associated with an audio buffer
+** Parameters: (sfx_audio_buf_t *) buf: The buffer whose associated memory
+** should be freed
+** Modifies : (sfx_audio_buf_t) *buf
+*/
+
+void
+sfx_audbuf_write(sfx_audio_buf_t *buf, unsigned char *src, int frames);
+/* Store data in an audion buffer
+** Parameters: (sfx_audio_buf_t *) buf: The buffer to write to
+** (unsigned char *) src: Pointer to the data that should be
+** written
+** (int) frames: Number of frames to write
+** Modifies : (sfx_audio_buf_t) *buf
+*/
+
+
+void
+sfx_audbuf_write_timestamp(sfx_audio_buf_t *buf, sfx_timestamp_t ts);
+/* Sets the most recently written timestamp for the buffer
+** Parameters: (sfx_audio_buf_t *) buf: The buffer to operate on
+** (sfx_timestamp_t) ts: The timestamp to set
+** If a timestamp is already set, 'ts' is checked for consistency and
+** 'silent' frames are introduced as padding for future writes.
+*/
+
+
+int
+sfx_audbuf_read_timestamp(sfx_audio_buf_t *buf, sfx_timestamp_t *ts);
+/* Reads the timestamp describing the time right before the next frame being read
+** Parameters: (sfx_audio_buf_t *) buf: The buffer to read from
+** Returns : (sfx_timestamp_t) *ts: The requested timestamp, or nothing
+** (int) zero on success, nonzero if no timestamp is known
+*/
+
+
+int
+sfx_audbuf_read(sfx_audio_buf_t *buf, unsigned char *dest, int frames);
+/* Read data from audio buffer
+** Parameters: (sfx_audio_buf_t *) buf: The buffer to write to
+** (unsigned char *) dest: Pointer to the place the read data
+** should be written to
+** (int) frames: Number of frames to write
+** Returns : (int) Number of frames actually read
+** Affects : (sfx_audio_buf_t) *buf
+** (unsigned char ) *dest
+** global error stream
+** If the returned number of frames is smaller than the number of frames
+** requested to be written, this function will issue a buffer underrun
+** warning and fill up the remaining space with the last frame it en--
+** countered, or a block of '0' if no such frame is known.
+*/
+
+
+
+
+#endif /* !_AUDIOBUF_H_ */
diff --git a/engines/sci/sfx/pcm_device/pcm_devices.c b/engines/sci/sfx/pcm_device/pcm_devices.c
new file mode 100644
index 0000000000..913b59fb08
--- /dev/null
+++ b/engines/sci/sfx/pcm_device/pcm_devices.c
@@ -0,0 +1,73 @@
+/***************************************************************************
+ pcmout.c Copyright (C) 2002 Solomon Peachy
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+#include <sfx_pcm.h>
+#include <resource.h>
+
+#ifndef NO_PCMOUT
+#ifdef SCUMMVM
+extern sfx_pcm_device_t sfx_pcm_driver_scummvm;
+#else // SCUMMVM
+# ifdef HAVE_SDL
+extern sfx_pcm_device_t sfx_pcm_driver_sdl;
+# endif
+# ifdef HAVE_ALSA
+extern sfx_pcm_device_t sfx_pcm_driver_alsa;
+# endif
+# ifdef _DREAMCAST
+extern sfx_pcm_device_t sfx_pcm_driver_dc;
+# endif
+#endif // SCUMMVM
+#endif
+
+sfx_pcm_device_t *pcmout_drivers[] = {
+#ifndef NO_PCMOUT
+#ifdef SCUMMVM
+ &sfx_pcm_driver_scummvm,
+#else // SCUMMVM
+# ifdef HAVE_SDL
+ &sfx_pcm_driver_sdl,
+# endif
+# ifdef HAVE_ALSA
+ &sfx_pcm_driver_alsa,
+# endif
+# ifdef _DREAMCAST
+ &sfx_pcm_driver_dc,
+# endif
+#endif // SCUMMVM
+#endif
+ NULL
+};
+
+sfx_pcm_device_t *
+sfx_pcm_find_device(char *name)
+{
+ int retval = 0;
+
+ if (!name) { /* Find default driver */
+ return pcmout_drivers[0];
+ }
+
+ while (pcmout_drivers[retval] &&
+ strcasecmp(name, pcmout_drivers[retval]->name))
+ retval++;
+
+ return pcmout_drivers[retval];
+}
+
diff --git a/engines/sci/sfx/pcm_device/scummvm.cpp b/engines/sci/sfx/pcm_device/scummvm.cpp
new file mode 100644
index 0000000000..1b64dd40d3
--- /dev/null
+++ b/engines/sci/sfx/pcm_device/scummvm.cpp
@@ -0,0 +1,60 @@
+#include "sfx_time.h"
+#include "sfx_pcm.h"
+#include "engines/engine.h"
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
+
+
+static int pcmout_scummvm_framesize;
+static Audio::AppendableAudioStream * pcmout_scummvm_audiostream;
+static Audio::SoundHandle pcmout_scummvm_sound_handle;
+
+
+static int pcmout_scummvm_init(sfx_pcm_device_t *self) {
+ int pcmout_scummvm_audiostream_flags = Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_STEREO;
+
+#ifdef SCUMM_LITTLE_ENDIAN
+ pcmout_scummvm_audiostream_flags |= Audio::Mixer::FLAG_LITTLE_ENDIAN;
+#endif
+
+ self->buf_size = 2048 << 1;
+ self->conf.rate = g_engine->_mixer->getOutputRate();
+ self->conf.stereo = SFX_PCM_STEREO_LR;
+ self->conf.format = SFX_PCM_FORMAT_S16_NATIVE;
+ pcmout_scummvm_framesize = SFX_PCM_FRAME_SIZE(self->conf);
+
+ pcmout_scummvm_audiostream = Audio::makeAppendableAudioStream(self->conf.rate, pcmout_scummvm_audiostream_flags);
+ ::g_engine->_mixer->playInputStream(Audio::Mixer::kSFXSoundType, &pcmout_scummvm_sound_handle, pcmout_scummvm_audiostream);
+
+ return SFX_OK;
+}
+
+static void pcmout_scummvm_exit(sfx_pcm_device_t *self) {
+}
+
+static int pcmout_scummvm_output(sfx_pcm_device_t *self, byte *buf, int count,
+ sfx_timestamp_t *timestamp) {
+
+ byte *__buf = new byte[count * pcmout_scummvm_framesize];
+
+ memcpy(__buf, buf, count * pcmout_scummvm_framesize);
+
+ pcmout_scummvm_audiostream->queueBuffer(__buf, count * pcmout_scummvm_framesize);
+
+ return SFX_OK;
+}
+
+
+sfx_pcm_device_t sfx_pcm_driver_scummvm = {
+ "ScummVM",
+ "0.1",
+ &pcmout_scummvm_init,
+ &pcmout_scummvm_exit,
+ NULL,
+ &pcmout_scummvm_output,
+ NULL,
+ {0, 0, 0},
+ 0,
+ NULL,
+ NULL
+ };
diff --git a/engines/sci/sfx/pcm_device/sdl.c b/engines/sci/sfx/pcm_device/sdl.c
new file mode 100644
index 0000000000..bd125835cb
--- /dev/null
+++ b/engines/sci/sfx/pcm_device/sdl.c
@@ -0,0 +1,272 @@
+/***************************************************************************
+ sdl.c Copyright (C) 2002 Solomon Peachy, Claudio Matsuoka,
+ 2003,04 Christoph Reichenbach
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+#include <sfx_pcm.h>
+#include "audiobuf.h"
+
+#ifdef HAVE_SDL
+
+#if !defined(_MSC_VER)
+# include <sys/time.h>
+#endif
+
+#include <SDL_config.h>
+#undef HAVE_ICONV
+#undef HAVE_ICONV_H
+#undef HAVE_ALLOCA_H
+
+#include <SDL.h>
+
+#define DELTA_TIME_LIMIT 10000 /* Report errors above this delta time */
+
+static sfx_audio_buf_t audio_buffer;
+static void (*sdl_sfx_timer_callback)(void *data);
+static void *sdl_sfx_timer_data;
+static int frame_size;
+static int buf_size;
+static int rate;
+static long last_callback_secs, last_callback_usecs;
+static int last_callback_len;
+
+static sfx_timestamp_t
+pcmout_sdl_output_timestamp(sfx_pcm_device_t *self)
+{
+ /* Number of frames enqueued in the output device: */
+ int delta = (buf_size - last_callback_len) / frame_size
+ /* Number of frames enqueued in the internal audio buffer: */
+ + audio_buffer.frames_nr;
+
+ return sfx_timestamp_add(sfx_new_timestamp(last_callback_secs,
+ last_callback_usecs,
+ rate),
+ delta);
+}
+
+FILE *fil = NULL;
+
+static void
+timer_sdl_internal_callback(void *userdata, byte *dest, int len)
+{
+ sci_gettime(&last_callback_secs, &last_callback_usecs);
+ last_callback_len = len;
+
+ if (sdl_sfx_timer_callback)
+ sdl_sfx_timer_callback(sdl_sfx_timer_data);
+
+#if 0
+ if (!sfx_audbuf_read_timestamp(&audio_buffer, &ts)) {
+ int delta = (buf_size - len) / frame_size;
+ sfx_timestamp_t real_ts;
+ long deltatime;
+ long sec2, usec2;
+ sci_gettime(&sec2, &usec2);
+
+ real_ts = sfx_timestamp_add(sfx_new_timestamp(sec, usec, rate), delta);
+
+ deltatime = sfx_timestamp_usecs_diff(ts, real_ts);
+
+ fprintf(stderr, "[SDL] Frames requested: %d Playing %ld too late. Needed %ldus for computations.\n",
+ len / frame_size, deltatime,
+ (usec2-usec) + (sec2-sec)*1000000);
+
+ if (abs(deltatime) > DELTA_TIME_LIMIT)
+ sciprintf("[SND:SDL] Very high delta time for PCM playback: %ld too late (%d frames in the future)\n",
+ deltatime, sfx_timestamp_frame_diff(sfx_new_timestamp(sec, usec, rate), ts));
+
+#if 0
+ if (deltatime < 0) {
+ /* Read and discard frames, explicitly underrunning */
+ int max_read = len / frame_size;
+ int frames_to_kill = sfx_timestamp_frame_diff(real_ts, ts);
+
+ while (frames_to_kill) {
+ int d = frames_to_kill > max_read? max_read : frames_to_kill;
+ sfx_audbuf_read(&audio_buffer, dest, d);
+ frames_to_kill -= d;
+ }
+ }
+#endif
+ }
+#endif
+ sfx_audbuf_read(&audio_buffer, dest, len / frame_size);
+
+#if 0
+ if (!fil) {
+ fil = fopen("/tmp/sdl.log", "w");
+ }
+ {
+ int i;
+ int end = len / frame_size;
+ gint16 *d = dest;
+ fprintf(fil, "Writing %d/%d\n", len, frame_size);
+ for (i = 0; i < end; i++) {
+ fprintf(fil, "\t%d\t%d\n", d[0], d[1]);
+ d += 2;
+ }
+ }
+#endif
+}
+
+
+static int
+pcmout_sdl_init(sfx_pcm_device_t *self)
+{
+ SDL_AudioSpec a;
+
+ if (SDL_Init(SDL_INIT_AUDIO|SDL_INIT_NOPARACHUTE) != 0) {
+ fprintf (stderr, "[SND:SDL] Error while initialising: %s\n", SDL_GetError());
+ return -1;
+ }
+
+ a.freq = 44100; /* FIXME */
+#ifdef WORDS_BIGENDIAN
+ a.format = AUDIO_S16MSB; /* FIXME */
+#else
+ a.format = AUDIO_S16LSB; /* FIXME */
+#endif
+ a.channels = 2; /* FIXME */
+ a.samples = 2048; /* FIXME */
+ a.callback = timer_sdl_internal_callback;
+ a.userdata = NULL;
+
+ if (SDL_OpenAudio (&a, NULL) < 0) {
+ fprintf (stderr, "[SND:SDL] Error while opening audio: %s\n", SDL_GetError());
+ return SFX_ERROR;
+ }
+
+ buf_size = a.samples;
+ rate = a.freq;
+
+ self->buf_size = a.samples << 1; /* Looks like they're using double size */
+ self->conf.rate = a.freq;
+ self->conf.stereo = a.channels > 1;
+ self->conf.format = SFX_PCM_FORMAT_S16_NATIVE;
+
+ frame_size = SFX_PCM_FRAME_SIZE(self->conf);
+
+ sfx_audbuf_init(&audio_buffer, self->conf);
+ SDL_PauseAudio (0);
+
+ return SFX_OK;
+}
+
+int
+pcmout_sdl_output(sfx_pcm_device_t *self, byte *buf,
+ int count, sfx_timestamp_t *ts)
+{
+ if (ts)
+ sfx_audbuf_write_timestamp(&audio_buffer, *ts);
+ sfx_audbuf_write(&audio_buffer, buf, count);
+ return SFX_OK;
+}
+
+
+static int
+pcmout_sdl_set_option(sfx_pcm_device_t *self, char *name, char *value)
+{
+ return SFX_ERROR; /* Option not supported */
+}
+
+static void
+pcmout_sdl_exit(sfx_pcm_device_t *self)
+{
+ SDL_PauseAudio (1);
+ SDL_CloseAudio();
+ sfx_audbuf_free(&audio_buffer);
+
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
+
+ if (!SDL_WasInit(SDL_INIT_EVERYTHING)) {
+ sciprintf("[SND:SDL] No active SDL subsystems found.. shutting down SDL\n");
+ SDL_Quit();
+ }
+}
+
+
+static int
+timer_sdl_set_option(char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+
+static int
+timer_sdl_init(void (*callback)(void *data), void *data)
+{
+ sdl_sfx_timer_callback = callback;
+ sdl_sfx_timer_data = data;
+
+
+ return SFX_OK;
+}
+
+static int
+timer_sdl_stop(void)
+{
+ sdl_sfx_timer_callback = NULL;
+
+ return SFX_OK;
+}
+
+static int
+timer_sdl_block(void)
+{
+ SDL_LockAudio();
+
+ return SFX_OK;
+}
+
+static int
+timer_sdl_unblock(void)
+{
+ SDL_UnlockAudio();
+
+ return SFX_OK;
+}
+
+#define SDL_PCM_VERSION "0.1"
+
+sfx_timer_t pcmout_sdl_timer = {
+ "sdl-pcm-timer",
+ SDL_PCM_VERSION,
+ 0,
+ 0,
+ timer_sdl_set_option,
+ timer_sdl_init,
+ timer_sdl_stop,
+ timer_sdl_block,
+ timer_sdl_unblock
+};
+
+sfx_pcm_device_t sfx_pcm_driver_sdl = {
+ "sdl",
+ SDL_PCM_VERSION,
+ pcmout_sdl_init,
+ pcmout_sdl_exit,
+ pcmout_sdl_set_option,
+ pcmout_sdl_output,
+ pcmout_sdl_output_timestamp,
+ {0, 0, 0},
+ 0,
+ &pcmout_sdl_timer,
+ NULL
+};
+
+#endif
diff --git a/engines/sci/sfx/player/Makefile.am b/engines/sci/sfx/player/Makefile.am
new file mode 100644
index 0000000000..34ae8e8427
--- /dev/null
+++ b/engines/sci/sfx/player/Makefile.am
@@ -0,0 +1,3 @@
+noinst_LIBRARIES = libsciplayer.a
+INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@
+libsciplayer_a_SOURCES = players.c realtime.c polled.c
diff --git a/engines/sci/sfx/player/players.c b/engines/sci/sfx/player/players.c
new file mode 100644
index 0000000000..d6d7cbaf17
--- /dev/null
+++ b/engines/sci/sfx/player/players.c
@@ -0,0 +1,54 @@
+/***************************************************************************
+ players.c Copyright (C) 2002..04 Christoph Reichenbach
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+
+#include <sfx_player.h>
+
+extern sfx_player_t sfx_player_realtime;
+extern sfx_player_t sfx_player_polled;
+
+sfx_player_t *sfx_players[] = {
+ &sfx_player_polled,
+ &sfx_player_realtime,
+ NULL
+};
+
+sfx_player_t *
+sfx_find_player(char *name)
+{
+ if (!name) {
+ /* Implement platform policy here */
+
+ return sfx_players[0];
+ } else {
+ int n = 0;
+ while (sfx_players[n] &&
+ strcasecmp(sfx_players[n]->name, name))
+ ++n;
+
+ return sfx_players[n];
+ }
+}
diff --git a/engines/sci/sfx/player/polled.c b/engines/sci/sfx/player/polled.c
new file mode 100644
index 0000000000..59eeceaac2
--- /dev/null
+++ b/engines/sci/sfx/player/polled.c
@@ -0,0 +1,335 @@
+/***************************************************************************
+ polled.c Copyright (C) 2004 Christoph Reichenbach
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+/* Polled player, mostly for PCM-based thingies (which _can_ poll, after all) */
+
+#include <sfx_player.h>
+#include "../softseq.h"
+#include "../mixer.h"
+
+static song_iterator_t *play_it;
+static int play_paused = 0;
+static sfx_softseq_t *seq;
+static int volume = 100;
+static sfx_timestamp_t new_timestamp;
+static int new_song = 0;
+
+/* The time counter is used to determine how close to the end of a tick we are.
+** For each frame played, it is decreased by 60. */
+#define TIME_INC 60
+static int time_counter = 0;
+
+static void
+pp_tell_synth(int buf_nr, byte *buf)
+{
+ seq->handle_command(seq, buf[0], buf_nr-1, buf+1);
+}
+
+
+/*----------------------*/
+/* Mixer implementation */
+/*----------------------*/
+int
+ppf_poll(sfx_pcm_feed_t *self, byte *dest, int size)
+{
+ int written = 0;
+ byte buf[4];
+ int buf_nr;
+
+ if (!play_it)
+ return 0;
+
+ if (play_paused)
+ return 0;
+
+ while (written < size) {
+ int can_play;
+ int do_play;
+
+ while (time_counter <= TIME_INC) {
+ int next_stat = songit_next(&play_it,
+ &(buf[0]), &buf_nr,
+ IT_READER_MASK_ALL
+ | IT_READER_MAY_FREE
+ | IT_READER_MAY_CLEAN);
+
+ switch (next_stat) {
+ case SI_PCM:
+ sfx_play_iterator_pcm(play_it, 0);
+ break;
+
+ case SI_FINISHED:
+ songit_free(play_it);
+ play_it = NULL;
+ return written; /* We're done... */
+
+ case SI_IGNORE:
+ case SI_LOOP:
+ case SI_RELATIVE_CUE:
+ case SI_ABSOLUTE_CUE:
+ break; /* Boooring... .*/
+
+ case 0: /* MIDI command */
+
+ seq->handle_command(seq, buf[0], buf_nr - 1, buf+1);
+ break;
+
+ default:
+ time_counter += next_stat * seq->pcm_conf.rate;
+ }
+ }
+
+ can_play = time_counter / TIME_INC;
+ do_play = (can_play > (size - written))? (size - written) : can_play;
+
+ time_counter -= do_play * TIME_INC;
+
+ seq->poll(seq, dest + written * self->frame_size, do_play);
+ written += do_play;
+ }
+
+ return size; /* Apparently, we wrote all that was requested */
+}
+
+void
+ppf_destroy(sfx_pcm_feed_t *self)
+{
+ /* no-op */
+}
+
+int
+ppf_get_timestamp(sfx_pcm_feed_t *self, sfx_timestamp_t *timestamp)
+{
+ if (!new_song)
+ return PCM_FEED_IDLE;
+
+ /* Otherwise, we have a timestamp: */
+
+ *timestamp = new_timestamp;
+ new_song = 0;
+ return PCM_FEED_TIMESTAMP;
+}
+
+extern sfx_player_t sfx_player_polled;
+static
+sfx_pcm_feed_t pcmfeed = {
+ ppf_poll,
+ ppf_destroy,
+ ppf_get_timestamp,
+ NULL,
+ {0, 0, 0},
+ "polled-player-feed",
+ 0,
+ 0 /* filled in by the mixer */
+};
+
+/*=======================*/
+/* Player implementation */
+/*=======================*/
+
+
+/*--------------------*/
+/* API implementation */
+/*--------------------*/
+
+static void
+pp_timer_callback(void)
+{
+ /* Hey, we're polled anyway ;-) */
+}
+
+static int
+pp_set_option(char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+static int
+pp_init(resource_mgr_t *resmgr, int expected_latency)
+{
+ resource_t *res = NULL, *res2 = NULL;
+ int fd;
+
+ if (!mixer)
+ return SFX_ERROR;
+
+ /* FIXME Temporary hack to detect Amiga games. */
+ fd = sci_open("bank.001", O_RDONLY);
+
+ if (fd == SCI_INVALID_FD)
+ seq = sfx_find_softseq(NULL);
+ else {
+ close(fd);
+ seq = sfx_find_softseq("amiga");
+ }
+
+ if (!seq) {
+ sciprintf("[sfx:seq:polled] Initialisation failed: Could not find software sequencer\n");
+ return SFX_ERROR;
+ }
+
+ if (seq->patch_nr != SFX_SEQ_PATCHFILE_NONE) {
+ res = scir_find_resource(resmgr, sci_patch, seq->patch_nr, 0);
+ }
+
+ if (seq->patch2_nr != SFX_SEQ_PATCHFILE_NONE) {
+ res2 = scir_find_resource(resmgr, sci_patch, seq->patch2_nr, 0);
+ }
+
+ if (seq->init(seq,
+ (res)? res->data : NULL,
+ (res)? res->size : 0,
+ (res2)? res2->data : NULL,
+ (res2)? res2->size : 0)) {
+ sciprintf("[sfx:seq:polled] Initialisation failed: Sequencer '%s', v%s failed to initialise\n",
+ seq->name, seq->version);
+ return SFX_ERROR;
+ }
+
+ pcmfeed.conf = seq->pcm_conf;
+
+ seq->set_volume(seq, volume);
+ mixer->subscribe(mixer, &pcmfeed);
+
+ sfx_player_polled.polyphony = seq->polyphony;
+ return SFX_OK;
+}
+
+static int
+pp_add_iterator(song_iterator_t *it, GTimeVal start_time)
+{
+ song_iterator_t *old = play_it;
+
+ SIMSG_SEND(it, SIMSG_SET_PLAYMASK(seq->playmask));
+ SIMSG_SEND(it, SIMSG_SET_RHYTHM(seq->play_rhythm));
+
+ if (play_it == NULL)
+ seq->allstop(seq);
+
+ play_it = sfx_iterator_combine(play_it, it);
+
+ seq->set_volume(seq, volume);
+
+ /* The check must happen HERE, and not at the beginning of the
+ function, to avoid a race condition with the mixer. */
+ if (old == NULL) {
+ new_timestamp = sfx_new_timestamp(start_time.tv_sec,
+ start_time.tv_usec,
+ seq->pcm_conf.rate);
+ /* ASAP otherwise */
+ time_counter = 0;
+ new_song = 1;
+ }
+
+ return SFX_OK;
+}
+
+static int
+pp_fade_out(void)
+{
+ fprintf(stderr, __FILE__": Attempt to fade out- not implemented yet\n");
+ return SFX_ERROR;
+}
+
+static int
+pp_stop(void)
+{
+ song_iterator_t *it = play_it;
+
+ play_it = NULL;
+fprintf(stderr, "[play] Now stopping it %p\n", (void *)it);
+ if (it)
+ songit_free(it);
+
+ seq->allstop(seq);
+
+ return SFX_OK;
+}
+
+static int
+pp_send_iterator_message(song_iterator_message_t msg)
+{
+ if (!play_it)
+ return SFX_ERROR;
+
+ songit_handle_message(&play_it, msg);
+ return SFX_OK;
+}
+
+static int
+pp_pause(void)
+{
+ play_paused = 1;
+ seq->set_volume(seq, 0);
+
+ return SFX_OK;
+}
+
+static int
+pp_resume(void)
+{
+ if (!play_it)
+ {
+ play_paused = 0;
+ return SFX_OK; /* Nothing to resume */
+ }
+
+ if (play_paused)
+ new_song = 1; /* Fake starting a new song, re-using the old
+ ** time stamp (now long in the past) to indicate
+ ** resuming ASAP */
+
+ play_paused = 0;
+ seq->set_volume(seq, volume);
+ return SFX_OK;
+}
+
+static int
+pp_exit(void)
+{
+ seq->exit(seq);
+ songit_free(play_it);
+ play_it = NULL;
+
+ return SFX_OK;
+}
+
+sfx_player_t sfx_player_polled = {
+ "polled",
+ "0.1",
+ &pp_set_option,
+ &pp_init,
+ &pp_add_iterator,
+ &pp_fade_out,
+ &pp_stop,
+ &pp_send_iterator_message,
+ &pp_pause,
+ &pp_resume,
+ &pp_exit,
+ &pp_timer_callback,
+ &pp_tell_synth,
+ 0 /* polyphony */
+};
diff --git a/engines/sci/sfx/player/realtime.c b/engines/sci/sfx/player/realtime.c
new file mode 100644
index 0000000000..752b0b816b
--- /dev/null
+++ b/engines/sci/sfx/player/realtime.c
@@ -0,0 +1,328 @@
+/***************************************************************************
+ realtime.c Copyright (C) 2002..04 Christoph Reichenbach
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+/* OK... 'realtime' may be a little too euphemistic, as this one just
+** prays for some reasonable amount of soft real-time, but it's close
+** enough, I guess. */
+
+#include <sfx_player.h>
+#include "../sequencer.h"
+
+static sfx_sequencer_t *seq;
+
+extern sfx_player_t sfx_player_realtime;
+
+/* Playing mechanism */
+
+static inline GTimeVal
+current_time(void)
+{
+ GTimeVal tv;
+ sci_get_current_time(&tv);
+ return tv;
+}
+
+static inline GTimeVal
+add_time_delta(GTimeVal tv, long delta)
+{
+ int sec_d;
+
+ tv.tv_usec += delta;
+ sec_d = tv.tv_usec / 1000000;
+ tv.tv_usec -= sec_d * 1000000;
+
+ tv.tv_sec += sec_d;
+
+ return tv;
+}
+
+static inline long
+delta_time(GTimeVal comp_tv, GTimeVal base)
+{
+ GTimeVal tv;
+ sci_get_current_time(&tv);
+ return (comp_tv.tv_sec - tv.tv_sec) * 1000000
+ + (comp_tv.tv_usec - tv.tv_usec);
+}
+
+static song_iterator_t *play_it = NULL;
+static GTimeVal play_last_time;
+static GTimeVal play_pause_started; /* Beginning of the last pause */
+static GTimeVal play_pause_counter; /* Last point in time to mark a
+ ** play position augmentation */
+static int play_paused = 0;
+static int play_it_done = 0;
+static int play_writeahead = 0;
+static int play_moredelay = 0;
+
+static void
+play_song(song_iterator_t *it, GTimeVal *wakeup_time, int writeahead_time)
+{
+ unsigned char buf[8];
+ int result;
+
+ if (play_paused) {
+ GTimeVal ct;
+ sci_get_current_time(&ct);
+
+ *wakeup_time =
+ add_time_delta(*wakeup_time, delta_time(play_pause_counter, ct));
+ play_pause_counter = ct;
+ } else
+ /* Not paused: */
+ while (play_it && delta_time(*wakeup_time, current_time())
+ < writeahead_time) {
+ int delay;
+
+ switch ((delay = songit_next(&(play_it),
+ &(buf[0]), &result,
+ IT_READER_MASK_ALL
+ | IT_READER_MAY_FREE
+ | IT_READER_MAY_CLEAN))) {
+
+ case SI_FINISHED:
+ play_it_done = 1;
+ return;
+
+ case SI_IGNORE:
+ case SI_LOOP:
+ case SI_RELATIVE_CUE:
+ case SI_ABSOLUTE_CUE:
+ break;
+
+ case SI_PCM:
+ sfx_play_iterator_pcm(play_it, 0);
+ break;
+
+ case 0:
+ seq->event(buf[0], result-1, buf+1);
+
+ break;
+
+ default:
+ play_moredelay = delay - 1;
+ *wakeup_time = song_next_wakeup_time(wakeup_time, delay);
+ if (seq->delay)
+ seq->delay(delay);
+ }
+ }
+}
+
+static void
+rt_tell_synth(int buf_nr, byte *buf)
+{
+ seq->event(buf[0], buf_nr-1, buf+1);
+}
+
+static void
+rt_timer_callback(void)
+{
+ if (play_it && !play_it_done) {
+ if (!play_moredelay) {
+ long delta = delta_time(play_last_time, current_time());
+
+ if (delta < 0) {
+ play_writeahead -= (int)((double)delta * 1.2); /* Adjust upwards */
+ } else if (delta > 15000) {
+ play_writeahead -= 2500; /* Adjust downwards */
+ }
+ } else --play_moredelay;
+
+ if (play_writeahead < seq->min_write_ahead_ms)
+ play_writeahead = seq->min_write_ahead_ms;
+
+ play_song(play_it, &play_last_time, play_writeahead);
+ }
+}
+
+static resource_t *
+find_patch(resource_mgr_t *resmgr, const char *seq, int patchfile)
+{
+ resource_t *res = NULL;
+
+ if (patchfile != SFX_SEQ_PATCHFILE_NONE) {
+ res = scir_find_resource(resmgr, sci_patch, patchfile, 0);
+ if (!res) {
+ fprintf(stderr, "[SFX] " __FILE__": patch.%03d requested by sequencer (%s), but not found\n",
+ patchfile, seq);
+ }
+ }
+
+ return res;
+}
+
+/* API implementation */
+
+static int
+rt_set_option(char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+static int
+rt_init(resource_mgr_t *resmgr, int expected_latency)
+{
+ resource_t *res = NULL, *res2 = NULL;
+ void *seq_dev = NULL;
+ GTimeVal foo = {0,0};
+
+ seq = sfx_find_sequencer(NULL);
+
+ if (!seq) {
+ fprintf(stderr, "[SFX] " __FILE__": Could not find sequencer\n");
+ return SFX_ERROR;
+ }
+
+ sfx_player_realtime.polyphony = seq->polyphony;
+
+ res = find_patch(resmgr, seq->name, seq->patchfile);
+ res2 = find_patch(resmgr, seq->name, seq->patchfile2);
+
+ if (seq->device)
+ seq_dev = sfx_find_device(seq->device, NULL);
+
+ if (seq->open(res? res->size : 0,
+ res? res->data : NULL,
+ res2? res2->size : 0,
+ res2? res2->data : NULL,
+ seq_dev)) {
+ fprintf(stderr, "[SFX] " __FILE__": Sequencer failed to initialize\n");
+ return SFX_ERROR;
+ }
+
+ play_writeahead = expected_latency;
+ if (play_writeahead < seq->min_write_ahead_ms)
+ play_writeahead = seq->min_write_ahead_ms;
+
+ play_writeahead *= 1000; /* microseconds */
+
+ if (seq->reset_timer)
+ seq->reset_timer(foo);
+
+ return SFX_OK;
+}
+
+static int
+rt_add_iterator(song_iterator_t *it, GTimeVal start_time)
+{
+ if (seq->reset_timer) /* Restart timer counting if possible */
+ seq->reset_timer(start_time);
+
+ SIMSG_SEND(it, SIMSG_SET_PLAYMASK(seq->playmask));
+ SIMSG_SEND(it, SIMSG_SET_RHYTHM(seq->play_rhythm));
+
+ play_last_time = start_time;
+ play_it = sfx_iterator_combine(play_it, it);
+ play_it_done = 0;
+ play_moredelay = 0;
+
+ return SFX_OK;
+}
+
+static int
+rt_fade_out(void)
+{
+ fprintf(stderr, __FILE__": Attempt to fade out- not implemented yet\n");
+ return SFX_ERROR;
+}
+
+static int
+rt_stop(void)
+{
+ song_iterator_t *it = play_it;
+
+ play_it = NULL;
+
+ if (it)
+ songit_free(it);
+ if (seq && seq->allstop)
+ seq->allstop();
+
+ return SFX_OK;
+}
+
+static int
+rt_send_iterator_message(song_iterator_message_t msg)
+{
+ if (!play_it)
+ return SFX_ERROR;
+
+ songit_handle_message(&play_it, msg);
+ return SFX_OK;
+}
+
+static int
+rt_pause(void)
+{
+ sci_get_current_time(&play_pause_started);
+ /* Also, indicate that we haven't modified the time counter
+ ** yet */
+ play_pause_counter = play_pause_started;
+
+ play_paused = 1;
+ if (!seq->allstop) {
+ sciprintf("[SFX] Cannot suspend sequencer, sound will continue for a bit\n");
+ return SFX_OK;
+ } else
+ return seq->allstop();
+}
+
+static int
+rt_resume(void)
+{
+ play_paused = 0;
+ return SFX_OK;
+}
+
+static int
+rt_exit(void)
+{
+ int retval = SFX_OK;
+
+ if(seq->close()) {
+ fprintf(stderr, "[SFX] Sequencer reported error on close\n");
+ retval = SFX_ERROR;
+ }
+
+ return retval;
+}
+
+sfx_player_t sfx_player_realtime = {
+ "realtime",
+ "0.1",
+ &rt_set_option,
+ &rt_init,
+ &rt_add_iterator,
+ &rt_fade_out,
+ &rt_stop,
+ &rt_send_iterator_message,
+ &rt_pause,
+ &rt_resume,
+ &rt_exit,
+ &rt_timer_callback,
+ &rt_tell_synth,
+ 0 /* polyphony */
+};
diff --git a/engines/sci/sfx/seq/Makefile.am b/engines/sci/sfx/seq/Makefile.am
new file mode 100644
index 0000000000..74fb4fda3f
--- /dev/null
+++ b/engines/sci/sfx/seq/Makefile.am
@@ -0,0 +1,5 @@
+noinst_LIBRARIES = libsciseq.a
+INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@
+EXTRA_DIST = instrument-map.h
+libsciseq_a_SOURCES = sequencers.c oss-adlib.c mt32.c gm.c instrument-map.c \
+ map-mt32-to-gm.c
diff --git a/engines/sci/sfx/seq/gm.c b/engines/sci/sfx/seq/gm.c
new file mode 100644
index 0000000000..4889e76ea8
--- /dev/null
+++ b/engines/sci/sfx/seq/gm.c
@@ -0,0 +1,185 @@
+/***************************************************************************
+ Copyright (C) 2008 Christoph Reichenbach
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantability,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <creichen@gmail.com>
+
+***************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include "../sequencer.h"
+#include "../device.h"
+#include "instrument-map.h"
+#include <resource.h>
+
+static midi_writer_t *writer = NULL;
+
+static int
+midi_gm_open(int patch_len, byte *data, int patch2_len, byte *data2, void *device)
+{
+ sfx_instrument_map_t *instrument_map = sfx_instrument_map_load_sci(data, patch_len);
+
+ if (!instrument_map) {
+ fprintf(stderr, "[GM] No GM instrument map found, trying MT-32 instrument map..\n");
+ instrument_map = sfx_instrument_map_mt32_to_gm(data2, patch2_len);
+ }
+
+ writer = sfx_mapped_writer((midi_writer_t *) device, instrument_map);
+
+ if (!writer)
+ return SFX_ERROR;
+
+ if (writer->reset_timer)
+ writer->reset_timer(writer);
+
+ return SFX_OK;
+}
+
+static int
+midi_gm_close(void)
+{
+ return SFX_OK;
+}
+
+static int
+midi_gm_event(byte command, int argc, byte *argv)
+{
+ byte data[4];
+
+ assert (argc < 4);
+ data[0] = command;
+ memcpy(data + 1, argv, argc);
+
+ writer->write(writer, data, argc + 1);
+
+ return SFX_OK;
+}
+
+static int
+midi_gm_delay(int ticks)
+{
+ writer->delay(writer, ticks);
+
+ return SFX_OK;
+}
+
+static int
+midi_gm_reset_timer(GTimeVal ts)
+{
+ writer->reset_timer(writer);
+
+ return SFX_OK;
+}
+
+#define MIDI_MASTER_VOLUME_LEN 8
+
+static int
+midi_gm_volume(guint8 volume)
+{
+ byte data[MIDI_MASTER_VOLUME_LEN] = {
+ 0xf0,
+ 0x7f,
+ 0x7f,
+ 0x04,
+ 0x01,
+ volume,
+ volume,
+ 0xf7};
+
+ writer->write(writer, data, MIDI_MASTER_VOLUME_LEN);
+ if (writer->flush)
+ writer->flush(writer);
+
+ return SFX_OK;
+}
+
+static int
+midi_gm_allstop(void)
+{
+ byte data[3] = { 0xb0,
+ 0x78, /* all sound off */
+ 0 };
+ int i;
+
+ /* All sound off on all channels */
+ for (i = 0; i < 16; i++) {
+ data[0] = 0xb0 | i;
+ writer->write(writer, data, 3);
+ }
+ if (writer->flush)
+ writer->flush(writer);
+
+ return SFX_OK;
+}
+
+static int
+midi_gm_reverb(int reverb)
+{
+ byte data[3] = { 0xb0,
+ 91, /* set reverb */
+ reverb };
+ int i;
+
+ /* Set reverb on all channels */
+ for (i = 0; i < 16; i++)
+ if (i != 9) {
+ data[0] = 0xb0 | i;
+ writer->write(writer, data, 3);
+ }
+ if (writer->flush)
+ writer->flush(writer);
+
+ return SFX_OK;
+}
+
+static int
+midi_gm_set_option(char *x, char *y)
+{
+ return SFX_ERROR;
+}
+
+sfx_sequencer_t sfx_sequencer_gm = {
+ "General MIDI",
+ "0.1",
+ SFX_DEVICE_MIDI,
+ &midi_gm_set_option,
+ &midi_gm_open,
+ &midi_gm_close,
+ &midi_gm_event,
+ &midi_gm_delay,
+ &midi_gm_reset_timer,
+ &midi_gm_allstop,
+ &midi_gm_volume,
+ &midi_gm_reverb,
+ 004, /* patch.004 */
+ 001, /* patch.001 */
+ 0x01, /* playflag */
+ 1, /* do play rhythm */
+ 64, /* max polyphony */
+ 0 /* no write-ahead needed inherently */
+};
diff --git a/engines/sci/sfx/seq/instrument-map.c b/engines/sci/sfx/seq/instrument-map.c
new file mode 100644
index 0000000000..0d829a0582
--- /dev/null
+++ b/engines/sci/sfx/seq/instrument-map.c
@@ -0,0 +1,539 @@
+/***************************************************************************
+ Copyright (C) 2008 Christoph Reichenbach
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantability,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <creichen@gmail.com>
+
+***************************************************************************/
+
+#include <assert.h>
+#include "sci_midi.h"
+#include "sci_memory.h"
+#include "instrument-map.h"
+#include "sfx_engine.h"
+
+sfx_instrument_map_t *
+sfx_instrument_map_new(int velocity_maps_nr)
+{
+ sfx_instrument_map_t *map = (sfx_instrument_map_t *)sci_malloc(sizeof (sfx_instrument_map_t));
+ int i;
+
+ map->initialisation_block_size = 0;
+ map->initialisation_block = NULL;
+
+ map->velocity_maps_nr = velocity_maps_nr;
+ map->percussion_velocity_map_index = SFX_NO_VELOCITY_MAP;
+
+ if (velocity_maps_nr == 0)
+ map->velocity_map = NULL; /* Yes, this complicates control flow needlessly, but it avoids some of the pointless
+ ** warnings that certain memory tools seem to find appropriate. */
+ else {
+ map->velocity_map = (byte **)sci_malloc(sizeof (byte *) * velocity_maps_nr);
+ for (i = 0; i < velocity_maps_nr; ++i)
+ map->velocity_map[i] = (byte *)sci_malloc(SFX_VELOCITIES_NR);
+ }
+ for (i = 0; i < SFX_INSTRUMENTS_NR; ++i)
+ map->velocity_map_index[i] = SFX_NO_VELOCITY_MAP;
+
+ map->percussion_volume_adjust = 0;
+ for (i = 0; i < SFX_RHYTHM_NR; ++i)
+ map->percussion_map[i] = i;
+
+
+ for (i = 0; i < SFX_INSTRUMENTS_NR; ++i) {
+ map->patch_map[i].patch = i;
+ map->patch_key_shift[i] = 0;
+ map->patch_volume_adjust[i] = 0;
+ }
+
+ return map;
+}
+
+void
+sfx_instrument_map_free(sfx_instrument_map_t *map)
+{
+ if (!map)
+ return;
+
+ if (map->velocity_map) {
+ int i;
+ for (i = 0; i < map->velocity_maps_nr; i++)
+ sci_free(map->velocity_map[i]);
+ sci_free(map->velocity_map);
+ map->velocity_map = NULL;
+ }
+
+ if (map->initialisation_block) {
+ sci_free(map->initialisation_block);
+ map->initialisation_block = NULL;
+ }
+
+ sci_free(map);
+}
+
+#define PATCH_MAP_OFFSET 0x0000
+#define PATCH_KEY_SHIFT_OFFSET 0x0080
+#define PATCH_VOLUME_ADJUST_OFFSET 0x0100
+#define PATCH_PERCUSSION_MAP_OFFSET 0x0180
+#define PATCH_PERCUSSION_VOLUME_ADJUST 0x0200
+#define PATCH_VELOCITY_MAP_INDEX 0x0201
+#define PATCH_VELOCITY_MAP(i) (0x0281 + (0x80 * i))
+#define PATCH_INIT_DATA_SIZE_LE 0x0481
+#define PATCH_INIT_DATA 0x0483
+
+#define PATCH_INSTRUMENT_MAPS_NR 4
+
+#define PATCH_MIN_SIZE PATCH_INIT_DATA
+
+
+static int
+patch001_type0_length(byte *data, size_t length)
+{
+ unsigned int pos = 492 + 246 * data[491];
+
+/* printf("timbres %d (post = %04x)\n",data[491], pos);*/
+
+ if ((length >= (pos + 386)) && (data[pos] == 0xAB) && (data[pos + 1] == 0xCD))
+ pos += 386;
+
+/* printf("pos = %04x (%02x %02x)\n", pos, data[pos], data[pos + 1]); */
+
+ if ((length >= (pos + 267)) && (data[pos] == 0xDC) && (data[pos + 1] == 0xBA))
+ pos += 267;
+
+/* printf("pos = %04x %04x (%d)\n", pos, length, pos-length); */
+
+
+ if (pos == length)
+ return 1;
+ return 0;
+}
+
+static int
+patch001_type1_length(byte *data, size_t length)
+{
+ if ((length >= 1155) && (((data[1154] << 8) + data[1153] + 1155) == length))
+ return 1;
+ return 0;
+}
+
+int
+sfx_instrument_map_detect(byte *data, size_t length)
+{
+ /* length test */
+ if (length < 1155)
+ return SFX_MAP_MT32;
+ if (length > 16889)
+ return SFX_MAP_MT32_GM;
+ if (patch001_type0_length(data, length) &&
+ !patch001_type1_length(data, length))
+ return SFX_MAP_MT32;
+ if (patch001_type1_length(data, length) &&
+ !patch001_type0_length(data, length))
+ return SFX_MAP_MT32_GM;
+ return SFX_MAP_UNKNOWN;
+}
+
+
+sfx_instrument_map_t *
+sfx_instrument_map_load_sci(byte *data, size_t size)
+{
+ sfx_instrument_map_t * map;
+ int i, m;
+
+ if (data == NULL)
+ return NULL;
+
+ if (size < PATCH_MIN_SIZE) {
+ fprintf(stderr, "[instrument-map] Instrument map too small: %d of %d\n", (int) size, PATCH_MIN_SIZE);
+ return NULL;
+ }
+
+ map = sfx_instrument_map_new(PATCH_INSTRUMENT_MAPS_NR);
+
+ /* Set up MIDI intialisation data */
+ map->initialisation_block_size = getInt16(data + PATCH_INIT_DATA_SIZE_LE);
+ if (map->initialisation_block_size) {
+ if (size < PATCH_MIN_SIZE + map->initialisation_block_size) {
+ fprintf(stderr, "[instrument-map] Instrument map too small for initialisation block: %d of %d\n", (int) size, PATCH_MIN_SIZE);
+ return NULL;
+ }
+
+ if (size > PATCH_MIN_SIZE + map->initialisation_block_size)
+ fprintf(stderr, "[instrument-map] Instrument larger than required by initialisation block: %d of %d\n", (int) size, PATCH_MIN_SIZE);
+
+ if (map->initialisation_block_size != 0) {
+ map->initialisation_block = (byte *)sci_malloc(map->initialisation_block_size);
+ memcpy(map->initialisation_block, data + PATCH_INIT_DATA, map->initialisation_block_size);
+ }
+ }
+
+ /* Set up basic instrument info */
+ for (i = 0; i < SFX_INSTRUMENTS_NR; i++) {
+ map->patch_map[i].patch = (char)data[PATCH_MAP_OFFSET + i];
+ map->patch_key_shift[i] = (char)data[PATCH_KEY_SHIFT_OFFSET + i];
+ map->patch_volume_adjust[i] = (char)data[PATCH_VOLUME_ADJUST_OFFSET + i];
+ map->patch_bend_range[i] = SFX_UNMAPPED;
+ map->velocity_map_index[i] = data[PATCH_VELOCITY_MAP_INDEX + i];
+ }
+
+ /* Set up percussion maps */
+ map->percussion_volume_adjust = data[PATCH_PERCUSSION_VOLUME_ADJUST];
+ for (i = 0; i < SFX_RHYTHM_NR; i++) {
+ map->percussion_map[i] = data[PATCH_PERCUSSION_MAP_OFFSET + i];
+ map->percussion_velocity_scale[i] = SFX_MAX_VELOCITY;
+ }
+
+ /* Set up velocity maps */
+ for (m = 0; m < PATCH_INSTRUMENT_MAPS_NR; m++) {
+ byte *velocity_map = map->velocity_map[m];
+ for (i = 0; i < SFX_VELOCITIES_NR; i++)
+ velocity_map[i] = data[PATCH_VELOCITY_MAP(m) + i];
+ }
+
+ map->percussion_velocity_map_index = 0;
+
+ return map;
+}
+
+
+/* Output with the instrument map */
+#define MIDI_CHANNELS_NR 0x10
+
+typedef struct decorated_midi_writer {
+ MIDI_WRITER_BODY
+
+ midi_writer_t *writer;
+ sfx_patch_map_t patches[MIDI_CHANNELS_NR];
+ sfx_instrument_map_t *map;
+} decorated_midi_writer_t;
+
+
+static void
+init_decorated(struct _midi_writer *self_)
+{
+ decorated_midi_writer_t *self = (decorated_midi_writer_t *) self_;
+ self->writer->init(self->writer);
+}
+
+static void
+set_option_decorated(struct _midi_writer *self_, char *name, char *value)
+{
+ decorated_midi_writer_t *self = (decorated_midi_writer_t *) self_;
+ self->writer->set_option(self->writer, name, value);
+}
+
+static void
+delay_decorated(struct _midi_writer *self_, int ticks)
+{
+ decorated_midi_writer_t *self = (decorated_midi_writer_t *) self_;
+ self->writer->delay(self->writer, ticks);
+}
+
+static void
+flush_decorated(struct _midi_writer *self_)
+{
+ decorated_midi_writer_t *self = (decorated_midi_writer_t *) self_;
+ if (self->writer->flush)
+ self->writer->flush(self->writer);
+}
+
+static void
+reset_timer_decorated(struct _midi_writer *self_)
+{
+ decorated_midi_writer_t *self = (decorated_midi_writer_t *) self_;
+ self->writer->reset_timer(self->writer);
+}
+
+
+static void
+close_decorated(decorated_midi_writer_t *self)
+{
+ sfx_instrument_map_free(self->map);
+ self->map = NULL;
+ self->writer->close(self->writer);
+ sci_free(self->name);
+ self->name = NULL;
+ sci_free(self);
+}
+
+#define BOUND_127(x) (((x) < 0)? 0 : (((x) > 0x7f)? 0x7f : (x)))
+
+static int
+bound_hard_127(int i, char *descr)
+{
+ int r = BOUND_127(i);
+ if (r != i)
+ fprintf(stderr, "[instrument-map] Hard-clipping %02x to %02x in %s\n", i, r, descr);
+ return r;
+}
+
+static int
+set_bend_range(midi_writer_t *writer, int channel, int range)
+{
+ byte buf[3] = {0xb0, 0x65, 0x00};
+
+ buf[0] |= channel & 0xf;
+ if (writer->write(writer, buf, 3) != SFX_OK)
+ return SFX_ERROR;
+
+ buf[1] = 0x64;
+ if (writer->write(writer, buf, 3) != SFX_OK)
+ return SFX_ERROR;
+
+ buf[1] = 0x06;
+ buf[2] = BOUND_127(range);
+ if (writer->write(writer, buf, 3) != SFX_OK)
+ return SFX_ERROR;
+
+ buf[1] = 0x26;
+ buf[2] = 0;
+ if (writer->write(writer, buf, 3) != SFX_OK)
+ return SFX_ERROR;
+
+ return SFX_OK;
+}
+
+static int
+write_decorated(decorated_midi_writer_t *self, byte *buf, int len)
+{
+ sfx_instrument_map_t *map = self->map;
+ int op = *buf & 0xf0;
+ int chan = *buf & 0x0f;
+ int patch = self->patches[chan].patch;
+ int rhythm = self->patches[chan].rhythm;
+
+ assert (len >= 1);
+
+ if (op == 0xC0 && chan != MIDI_RHYTHM_CHANNEL) { /* Program change */
+ int patch = bound_hard_127(buf[1], "program change");
+ int instrument = map->patch_map[patch].patch;
+ int bend_range = map->patch_bend_range[patch];
+
+ self->patches[chan] = map->patch_map[patch];
+
+ if (instrument == SFX_UNMAPPED || instrument == SFX_MAPPED_TO_RHYTHM)
+ return SFX_OK;
+
+ assert (len >= 2);
+ buf[1] = bound_hard_127(instrument, "patch lookup");
+
+ if (self->writer->write(self->writer, buf, len) != SFX_OK)
+ return SFX_ERROR;
+
+ if (bend_range != SFX_UNMAPPED)
+ return set_bend_range(self->writer, chan, bend_range);
+
+ return SFX_OK;
+ }
+
+ if (chan == MIDI_RHYTHM_CHANNEL || patch == SFX_MAPPED_TO_RHYTHM) {
+ /* Rhythm channel handling */
+ switch (op) {
+ case 0x80:
+ case 0x90: { /* Note off / note on */
+ int velocity, instrument, velocity_map_index, velocity_scale;
+
+ if (patch == SFX_MAPPED_TO_RHYTHM) {
+ buf[0] = (buf[0] & ~0x0f) | MIDI_RHYTHM_CHANNEL;
+ instrument = rhythm;
+ velocity_scale = SFX_MAX_VELOCITY;
+ } else {
+ int instrument_index = bound_hard_127(buf[1], "rhythm instrument index");
+ instrument = map->percussion_map[instrument_index];
+ velocity_scale = map->percussion_velocity_scale[instrument_index];
+ }
+
+ if (instrument == SFX_UNMAPPED)
+ return SFX_OK;
+
+ assert (len >= 3);
+
+ velocity = bound_hard_127(buf[2], "rhythm velocity");
+ velocity_map_index = map->percussion_velocity_map_index;
+
+ if (velocity_map_index != SFX_NO_VELOCITY_MAP)
+ velocity = BOUND_127(velocity + map->velocity_map[velocity_map_index][velocity]);
+
+ velocity = BOUND_127(velocity * velocity_scale / SFX_MAX_VELOCITY);
+
+ buf[1] = bound_hard_127(instrument, "rhythm instrument");
+ buf[2] = velocity;
+
+ break;
+ }
+
+ case 0xB0: { /* Controller change */
+ assert (len >= 3);
+ if (buf[1] == 0x7) /* Volume change */
+ buf[2] = BOUND_127(buf[2] + map->percussion_volume_adjust);
+ break;
+ }
+
+ default: break;
+ }
+
+ } else {
+ /* Instrument channel handling */
+
+ if (patch == SFX_UNMAPPED)
+ return SFX_OK;
+
+ switch (op) {
+ case 0x80:
+ case 0x90: { /* Note off / note on */
+ int note = bound_hard_127(buf[1], "note");
+ int velocity = bound_hard_127(buf[2], "velocity");
+ int velocity_map_index = map->velocity_map_index[patch];
+ assert (len >= 3);
+
+ note += map->patch_key_shift[patch];
+ /* Not the most efficient solutions, but the least error-prone */
+ while (note < 0)
+ note += 12;
+ while (note > 0x7f)
+ note -= 12;
+
+ if (velocity_map_index != SFX_NO_VELOCITY_MAP)
+ velocity = BOUND_127(velocity + map->velocity_map[velocity_map_index][velocity]);
+
+ buf[1] = note;
+ buf[2] = velocity;
+ break;
+ }
+
+ case 0xB0: /* Controller change */
+ assert (len >= 3);
+ if (buf[1] == 0x7) /* Volume change */
+ buf[2] = BOUND_127(buf[2] + map->patch_volume_adjust[patch]);
+ break;
+
+ default: break;
+ }
+ }
+
+ return self->writer->write(self->writer, buf, len);
+}
+
+#define MIDI_BYTES_PER_SECOND 3250 /* This seems to be the minimum guarantee by the standard */
+#define MAX_PER_TICK (MIDI_BYTES_PER_SECOND / 60) /* After this, we ought to issue one tick of pause */
+
+static void
+init(midi_writer_t *writer, byte *data, size_t len)
+{
+ int offset = 0;
+ byte status = 0;
+
+ /* Send init data as separate MIDI commands */
+ while (offset < len) {
+ int args;
+ byte op = data[offset];
+ byte msg[3];
+ int i;
+
+ if (op == 0xf0) {
+ int msg_len;
+ byte *find = (byte *) memchr(data + offset, 0xf7, len - offset);
+
+ if (!find) {
+ fprintf(stderr, "[instrument-map] Failed to find end of sysex message\n");
+ return;
+ }
+
+ msg_len = find - data - offset + 1;
+ writer->write(writer, data + offset, msg_len);
+
+ /* Wait at least 40ms after sysex */
+ writer->delay(writer, 3);
+ offset += msg_len;
+ continue;
+ }
+
+ if (op < 0x80)
+ op = status;
+ else {
+ status = op;
+ offset++;
+ }
+
+ msg[0] = op;
+
+ switch (op & 0xf0) {
+ case 0xc0:
+ case 0xd0:
+ args = 1;
+ break;
+ default:
+ args = 2;
+ }
+
+ if (args > len - offset) {
+ fprintf(stderr, "[instrument-map] Insufficient bytes remaining for MIDI command %02x\n", op);
+ return;
+ }
+
+ for (i = 0; i < args; i++)
+ msg[i + 1] = data[offset + i];
+
+ writer->write(writer, msg, args + 1);
+ offset += args;
+
+ if (writer->flush)
+ writer->flush(writer);
+ }
+}
+
+#define NAME_SUFFIX "+instruments"
+
+midi_writer_t *
+sfx_mapped_writer(midi_writer_t *writer, sfx_instrument_map_t *map)
+{
+ int i;
+ decorated_midi_writer_t *retval;
+
+ if (map == NULL)
+ return writer;
+
+ retval = (decorated_midi_writer_t *)sci_malloc(sizeof(decorated_midi_writer_t));
+ retval->writer = writer;
+ retval->name = (char *)sci_malloc(strlen(writer->name) + strlen(NAME_SUFFIX) + 1);
+ strcpy(retval->name, writer->name);
+ strcat(retval->name, NAME_SUFFIX);
+
+ retval->init = (int (*)(midi_writer_t *)) init_decorated;
+ retval->set_option = (int (*)(midi_writer_t *, char *, char *)) set_option_decorated;
+ retval->write = (int (*)(midi_writer_t *, byte *, int)) write_decorated;
+ retval->delay = (void (*)(midi_writer_t *, int)) delay_decorated;
+ retval->flush = (void (*)(midi_writer_t *)) flush_decorated;
+ retval->reset_timer = (void (*)(midi_writer_t *)) reset_timer_decorated;
+ retval->close = (void (*)(midi_writer_t *)) close_decorated;
+
+ retval->map = map;
+
+ init(writer, map->initialisation_block, map->initialisation_block_size);
+
+ for (i = 0; i < MIDI_CHANNELS_NR; i++)
+ retval->patches[i].patch = SFX_UNMAPPED;
+
+ return (midi_writer_t *) retval;
+}
+
diff --git a/engines/sci/sfx/seq/instrument-map.h b/engines/sci/sfx/seq/instrument-map.h
new file mode 100644
index 0000000000..85f654d60b
--- /dev/null
+++ b/engines/sci/sfx/seq/instrument-map.h
@@ -0,0 +1,132 @@
+/***************************************************************************
+ Copyright (C) 2008 Christoph Reichenbach
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantability,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <creichen@gmail.com>
+
+***************************************************************************/
+
+/* Implementation of SCI instrument maps for GM and MT-32. */
+
+#ifndef SCI_INSTRUMENT_MAP_
+#define SCI_INSTRUMENT_MAP_
+
+#include <stdlib.h>
+#include "resource.h"
+#include "../device.h"
+
+#define SFX_INSTRUMENTS_NR 0x80
+#define SFX_RHYTHM_NR 0x80
+#define SFX_VELOCITIES_NR 0x80
+#define SFX_NO_VELOCITY_MAP -1 /* use in velocity_map_index to indicate that no map should be used */
+
+/* Instrument map types */
+#define SFX_MAP_UNKNOWN 0
+#define SFX_MAP_MT32 1 /* Original MT-32 map format */
+#define SFX_MAP_MT32_GM 2 /* More recent map format used for both MT-32 and GM */
+
+/* Patch not mapped */
+#define SFX_UNMAPPED -1
+/* Patch mapped to rhythm key */
+#define SFX_MAPPED_TO_RHYTHM -2
+
+/* Maximum velocity (used for scaling) */
+#define SFX_MAX_VELOCITY 128
+
+typedef struct {
+ int patch; /* Native instrument, SFX_UNMAPPED or SFX_MAPPED_TO_RHYTHM */
+ int rhythm; /* Rhythm key when patch == SFX_MAPPED_TO_RHYTHM */
+} sfx_patch_map_t;
+
+typedef struct {
+ sfx_patch_map_t patch_map[SFX_INSTRUMENTS_NR]; /* Map patch nr to which native instrument or rhythm key */
+ int patch_key_shift[SFX_INSTRUMENTS_NR]; /* Shift patch key by how much? */
+ int patch_volume_adjust[SFX_INSTRUMENTS_NR]; /* Adjust controller 7 by how much? */
+ int patch_bend_range[SFX_INSTRUMENTS_NR]; /* Bend range in semitones or SFX_UNMAPPED for default */
+
+ int percussion_map[SFX_RHYTHM_NR]; /* Map percussion instrument (RHYTH_CHANNEL) to what native 'key'? */
+ int percussion_volume_adjust; /* unused in SCI patches */
+
+ int velocity_map_index[SFX_INSTRUMENTS_NR]; /* Velocity translation map to use for that instrument */
+ int velocity_maps_nr; /* How many velocity translation maps do we have? */
+ byte **velocity_map; /* velocity_maps_nr entries, each of size SFX_VELOCITIES_NR */
+ int percussion_velocity_map_index; /* Special index for the percussion map */
+ int percussion_velocity_scale[SFX_INSTRUMENTS_NR]; /* Velocity scale (0 - SFX_PERC_MAX_VOL) */
+
+ size_t initialisation_block_size;
+ byte *initialisation_block; /* Initial MIDI commands to set up the device */
+} sfx_instrument_map_t;
+
+sfx_instrument_map_t *
+sfx_instrument_map_new(int velocity_maps_nr);
+/* Constructs a new default-initialised velocity map
+** Parameters: (int) velocity_maps_nr: Number of velocity maps to allocate
+** Returns : (sfx_instrument_map *) an initialised instrument map
+*/
+
+void
+sfx_instrument_map_free(sfx_instrument_map_t *map);
+/* Deallocates an instrument map
+** Parameters: (sfx_instrument_map *) map: The map to deallocate, or NULL for a no-op
+*/
+
+sfx_instrument_map_t *
+sfx_instrument_map_load_sci(byte *data, size_t length);
+/* Allocate and initialise an instrument map from SCI data
+** Parameters: (byte *) Pointer to the data to initialise from
+** (size_t) Number of bytes to expect within
+** Returns : (sfx_instrument_map_t *) An initialised instrument map for these settings, or NULL
+** if `data' is NULL or `data' and `length' do not permit a valid instrument map
+** If `data' is null, the function will return NULL quietly.
+*/
+
+sfx_instrument_map_t *
+sfx_instrument_map_mt32_to_gm(byte *data, size_t size);
+/* Allocate and initialise an instrument map from MT-32 patch data
+** Parameters: (byte *) Pointer to the MT-32 patch data to initialise from
+** (size_t) Number of bytes to expect within
+** Returns : (sfx_instrument_map_t *) An initialised instrument map for these settings
+** If `data' is null or invalid, the function will return a default MT-32 to GM map.
+*/
+
+int
+sfx_instrument_map_detect(byte *data, size_t size);
+/* Detects the type of patch data
+** Parameters: (byte *) Pointer to the patch data
+** (size_t) Number of bytes to expect within
+** Returns : (int) SFX_MAP_SCI1 for an SCI1 instrument map, SFX_MAP_SCI0_MT32 for SCI0 MT-32 patch data,
+** or SFX_MAP_UNKNOWN for unknown.
+*/
+
+midi_writer_t *
+sfx_mapped_writer(midi_writer_t *writer, sfx_instrument_map_t *map);
+/* Wrap a midi_writer_t into an instrument map
+** Parameters: (midi_writer_t *) writer: The writer to wrap
+** (sfx_instrument_map_t *) map: The map to apply to all commands going into the writer, or NULL
+** Returns : (midi_writer_t *) A MIDI writer that preprocesses all data by `map' and otherwise relies on `writer'
+** Effects : If successful and neccessary, this operation will send initialisation messages to the writer, as needed.
+** If `map' is NULL, this returns `writer'. Otherwise it sets up a Decorator that handles translation and automatically
+** deallocates the instrument map when the writer is closed.
+*/
+
+
+#endif /* !defined(SCI_INSTRUMENT_MAP_) */
diff --git a/engines/sci/sfx/seq/map-mt32-to-gm.c b/engines/sci/sfx/seq/map-mt32-to-gm.c
new file mode 100644
index 0000000000..bcaf3556f1
--- /dev/null
+++ b/engines/sci/sfx/seq/map-mt32-to-gm.c
@@ -0,0 +1,813 @@
+/***************************************************************************
+ map-mt32-to-gm.c (C) 1999,2001 Christoph Reichenbach, TU Darmstadt
+ (C) 1999-2000 Rickard Lind
+ (C) 2008 Walter van Niftrik
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CJR) [creichen@rbg.informatik.tu-darmstadt.de]
+
+ Roland MT-32 to General MIDI conversion:
+
+ Rickard Lind [rpl@dd.chalmers.se]
+
+***************************************************************************/
+
+#include <sciresource.h>
+#include <engine.h>
+#include <stdarg.h>
+#include "instrument-map.h"
+
+#define DEBUG_MT32_TO_GM
+
+static char
+*GM_Instrument_Names[] = {
+ /*000*/ "Acoustic Grand Piano",
+ /*001*/ "Bright Acoustic Piano",
+ /*002*/ "Electric Grand Piano",
+ /*003*/ "Honky-tonk Piano",
+ /*004*/ "Electric Piano 1",
+ /*005*/ "Electric Piano 2",
+ /*006*/ "Harpsichord",
+ /*007*/ "Clavinet",
+ /*008*/ "Celesta",
+ /*009*/ "Glockenspiel",
+ /*010*/ "Music Box",
+ /*011*/ "Vibraphone",
+ /*012*/ "Marimba",
+ /*013*/ "Xylophone",
+ /*014*/ "Tubular Bells",
+ /*015*/ "Dulcimer",
+ /*016*/ "Drawbar Organ",
+ /*017*/ "Percussive Organ",
+ /*018*/ "Rock Organ",
+ /*019*/ "Church Organ",
+ /*020*/ "Reed Organ",
+ /*021*/ "Accordion",
+ /*022*/ "Harmonica",
+ /*023*/ "Tango Accordion",
+ /*024*/ "Acoustic Guitar (nylon)",
+ /*025*/ "Acoustic Guitar (steel)",
+ /*026*/ "Electric Guitar (jazz)",
+ /*027*/ "Electric Guitar (clean)",
+ /*028*/ "Electric Guitar (muted)",
+ /*029*/ "Overdriven Guitar",
+ /*030*/ "Distortion Guitar",
+ /*031*/ "Guitar Harmonics",
+ /*032*/ "Acoustic Bass",
+ /*033*/ "Electric Bass (finger)",
+ /*034*/ "Electric Bass (pick)",
+ /*035*/ "Fretless Bass",
+ /*036*/ "Slap Bass 1",
+ /*037*/ "Slap Bass 2",
+ /*038*/ "Synth Bass 1",
+ /*039*/ "Synth Bass 2",
+ /*040*/ "Violin",
+ /*041*/ "Viola",
+ /*042*/ "Cello",
+ /*043*/ "Contrabass",
+ /*044*/ "Tremolo Strings",
+ /*045*/ "Pizzicato Strings",
+ /*046*/ "Orchestral Harp",
+ /*047*/ "Timpani",
+ /*048*/ "String Ensemble 1",
+ /*049*/ "String Ensemble 2",
+ /*050*/ "SynthStrings 1",
+ /*051*/ "SynthStrings 2",
+ /*052*/ "Choir Aahs",
+ /*053*/ "Voice Oohs",
+ /*054*/ "Synth Voice",
+ /*055*/ "Orchestra Hit",
+ /*056*/ "Trumpet",
+ /*057*/ "Trombone",
+ /*058*/ "Tuba",
+ /*059*/ "Muted Trumpet",
+ /*060*/ "French Horn",
+ /*061*/ "Brass Section",
+ /*062*/ "SynthBrass 1",
+ /*063*/ "SynthBrass 2",
+ /*064*/ "Soprano Sax",
+ /*065*/ "Alto Sax",
+ /*066*/ "Tenor Sax",
+ /*067*/ "Baritone Sax",
+ /*068*/ "Oboe",
+ /*069*/ "English Horn",
+ /*070*/ "Bassoon",
+ /*071*/ "Clarinet",
+ /*072*/ "Piccolo",
+ /*073*/ "Flute",
+ /*074*/ "Recorder",
+ /*075*/ "Pan Flute",
+ /*076*/ "Blown Bottle",
+ /*077*/ "Shakuhachi",
+ /*078*/ "Whistle",
+ /*079*/ "Ocarina",
+ /*080*/ "Lead 1 (square)",
+ /*081*/ "Lead 2 (sawtooth)",
+ /*082*/ "Lead 3 (calliope)",
+ /*083*/ "Lead 4 (chiff)",
+ /*084*/ "Lead 5 (charang)",
+ /*085*/ "Lead 6 (voice)",
+ /*086*/ "Lead 7 (fifths)",
+ /*087*/ "Lead 8 (bass+lead)",
+ /*088*/ "Pad 1 (new age)",
+ /*089*/ "Pad 2 (warm)",
+ /*090*/ "Pad 3 (polysynth)",
+ /*091*/ "Pad 4 (choir)",
+ /*092*/ "Pad 5 (bowed)",
+ /*093*/ "Pad 6 (metallic)",
+ /*094*/ "Pad 7 (halo)",
+ /*095*/ "Pad 8 (sweep)",
+ /*096*/ "FX 1 (rain)",
+ /*097*/ "FX 2 (soundtrack)",
+ /*098*/ "FX 3 (crystal)",
+ /*099*/ "FX 4 (atmosphere)",
+ /*100*/ "FX 5 (brightness)",
+ /*101*/ "FX 6 (goblins)",
+ /*102*/ "FX 7 (echoes)",
+ /*103*/ "FX 8 (sci-fi)",
+ /*104*/ "Sitar",
+ /*105*/ "Banjo",
+ /*106*/ "Shamisen",
+ /*107*/ "Koto",
+ /*108*/ "Kalimba",
+ /*109*/ "Bag pipe",
+ /*110*/ "Fiddle",
+ /*111*/ "Shannai",
+ /*112*/ "Tinkle Bell",
+ /*113*/ "Agogo",
+ /*114*/ "Steel Drums",
+ /*115*/ "Woodblock",
+ /*116*/ "Taiko Drum",
+ /*117*/ "Melodic Tom",
+ /*118*/ "Synth Drum",
+ /*119*/ "Reverse Cymbal",
+ /*120*/ "Guitar Fret Noise",
+ /*121*/ "Breath Noise",
+ /*122*/ "Seashore",
+ /*123*/ "Bird Tweet",
+ /*124*/ "Telephone Ring",
+ /*125*/ "Helicopter",
+ /*126*/ "Applause",
+ /*127*/ "Gunshot"
+};
+
+/* The GM Percussion map is downwards compatible to the MT32 map, which is used in SCI */
+static char
+*GM_Percussion_Names[] = {
+ /*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /*20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /*30*/ 0, 0, 0, 0,
+/* The preceeding percussions are not covered by the GM standard */
+ /*34*/ "Acoustic Bass Drum",
+ /*35*/ "Bass Drum 1",
+ /*36*/ "Side Stick",
+ /*37*/ "Acoustic Snare",
+ /*38*/ "Hand Clap",
+ /*39*/ "Electric Snare",
+ /*40*/ "Low Floor Tom",
+ /*41*/ "Closed Hi-Hat",
+ /*42*/ "High Floor Tom",
+ /*43*/ "Pedal Hi-Hat",
+ /*44*/ "Low Tom",
+ /*45*/ "Open Hi-Hat",
+ /*46*/ "Low-Mid Tom",
+ /*47*/ "Hi-Mid Tom",
+ /*48*/ "Crash Cymbal 1",
+ /*49*/ "High Tom",
+ /*50*/ "Ride Cymbal 1",
+ /*51*/ "Chinese Cymbal",
+ /*52*/ "Ride Bell",
+ /*53*/ "Tambourine",
+ /*54*/ "Splash Cymbal",
+ /*55*/ "Cowbell",
+ /*56*/ "Crash Cymbal 2",
+ /*57*/ "Vibraslap",
+ /*58*/ "Ride Cymbal 2",
+ /*59*/ "Hi Bongo",
+ /*60*/ "Low Bongo",
+ /*61*/ "Mute Hi Conga",
+ /*62*/ "Open Hi Conga",
+ /*63*/ "Low Conga",
+ /*64*/ "High Timbale",
+ /*65*/ "Low Timbale",
+ /*66*/ "High Agogo",
+ /*67*/ "Low Agogo",
+ /*68*/ "Cabasa",
+ /*69*/ "Maracas",
+ /*70*/ "Short Whistle",
+ /*71*/ "Long Whistle",
+ /*72*/ "Short Guiro",
+ /*73*/ "Long Guiro",
+ /*74*/ "Claves",
+ /*75*/ "Hi Wood Block",
+ /*76*/ "Low Wood Block",
+ /*77*/ "Mute Cuica",
+ /*78*/ "Open Cuica",
+ /*79*/ "Mute Triangle",
+ /*80*/ "Open Triangle"
+};
+
+/*******************************************
+ * Fancy instrument mappings begin here... *
+ *******************************************/
+
+
+static struct {
+ char *name;
+ gint8 gm_instr;
+ gint8 gm_rhythm_key;
+} MT32_PresetTimbreMaps[] = {
+ /*000*/ {"AcouPiano1", 0, SFX_UNMAPPED},
+ /*001*/ {"AcouPiano2", 1, SFX_UNMAPPED},
+ /*002*/ {"AcouPiano3", 0, SFX_UNMAPPED},
+ /*003*/ {"ElecPiano1", 4, SFX_UNMAPPED},
+ /*004*/ {"ElecPiano2", 5, SFX_UNMAPPED},
+ /*005*/ {"ElecPiano3", 4, SFX_UNMAPPED},
+ /*006*/ {"ElecPiano4", 5, SFX_UNMAPPED},
+ /*007*/ {"Honkytonk ", 3, SFX_UNMAPPED},
+ /*008*/ {"Elec Org 1", 16, SFX_UNMAPPED},
+ /*009*/ {"Elec Org 2", 17, SFX_UNMAPPED},
+ /*010*/ {"Elec Org 3", 18, SFX_UNMAPPED},
+ /*011*/ {"Elec Org 4", 18, SFX_UNMAPPED},
+ /*012*/ {"Pipe Org 1", 19, SFX_UNMAPPED},
+ /*013*/ {"Pipe Org 2", 19, SFX_UNMAPPED},
+ /*014*/ {"Pipe Org 3", 20, SFX_UNMAPPED},
+ /*015*/ {"Accordion ", 21, SFX_UNMAPPED},
+ /*016*/ {"Harpsi 1 ", 6, SFX_UNMAPPED},
+ /*017*/ {"Harpsi 2 ", 6, SFX_UNMAPPED},
+ /*018*/ {"Harpsi 3 ", 6, SFX_UNMAPPED},
+ /*019*/ {"Clavi 1 ", 7, SFX_UNMAPPED},
+ /*020*/ {"Clavi 2 ", 7, SFX_UNMAPPED},
+ /*021*/ {"Clavi 3 ", 7, SFX_UNMAPPED},
+ /*022*/ {"Celesta 1 ", 8, SFX_UNMAPPED},
+ /*023*/ {"Celesta 2 ", 8, SFX_UNMAPPED},
+ /*024*/ {"Syn Brass1", 62, SFX_UNMAPPED},
+ /*025*/ {"Syn Brass2", 63, SFX_UNMAPPED},
+ /*026*/ {"Syn Brass3", 62, SFX_UNMAPPED},
+ /*027*/ {"Syn Brass4", 63, SFX_UNMAPPED},
+ /*028*/ {"Syn Bass 1", 38, SFX_UNMAPPED},
+ /*029*/ {"Syn Bass 2", 39, SFX_UNMAPPED},
+ /*030*/ {"Syn Bass 3", 38, SFX_UNMAPPED},
+ /*031*/ {"Syn Bass 4", 39, SFX_UNMAPPED},
+ /*032*/ {"Fantasy ", 88, SFX_UNMAPPED},
+ /*033*/ {"Harmo Pan ", 89, SFX_UNMAPPED},
+ /*034*/ {"Chorale ", 52, SFX_UNMAPPED},
+ /*035*/ {"Glasses ", 98, SFX_UNMAPPED},
+ /*036*/ {"Soundtrack", 97, SFX_UNMAPPED},
+ /*037*/ {"Atmosphere", 99, SFX_UNMAPPED},
+ /*038*/ {"Warm Bell ", 89, SFX_UNMAPPED},
+ /*039*/ {"Funny Vox ", 85, SFX_UNMAPPED},
+ /*040*/ {"Echo Bell ", 39, SFX_UNMAPPED},
+ /*041*/ {"Ice Rain ", 101, SFX_UNMAPPED},
+ /*042*/ {"Oboe 2001 ", 68, SFX_UNMAPPED},
+ /*043*/ {"Echo Pan ", 87, SFX_UNMAPPED},
+ /*044*/ {"DoctorSolo", 86, SFX_UNMAPPED},
+ /*045*/ {"Schooldaze", 103, SFX_UNMAPPED},
+ /*046*/ {"BellSinger", 88, SFX_UNMAPPED},
+ /*047*/ {"SquareWave", 80, SFX_UNMAPPED},
+ /*048*/ {"Str Sect 1", 48, SFX_UNMAPPED},
+ /*049*/ {"Str Sect 2", 48, SFX_UNMAPPED},
+ /*050*/ {"Str Sect 3", 49, SFX_UNMAPPED},
+ /*051*/ {"Pizzicato ", 45, SFX_UNMAPPED},
+ /*052*/ {"Violin 1 ", 40, SFX_UNMAPPED},
+ /*053*/ {"Violin 2 ", 40, SFX_UNMAPPED},
+ /*054*/ {"Cello 1 ", 42, SFX_UNMAPPED},
+ /*055*/ {"Cello 2 ", 42, SFX_UNMAPPED},
+ /*056*/ {"Contrabass", 43, SFX_UNMAPPED},
+ /*057*/ {"Harp 1 ", 46, SFX_UNMAPPED},
+ /*058*/ {"Harp 2 ", 46, SFX_UNMAPPED},
+ /*059*/ {"Guitar 1 ", 24, SFX_UNMAPPED},
+ /*060*/ {"Guitar 2 ", 25, SFX_UNMAPPED},
+ /*061*/ {"Elec Gtr 1", 26, SFX_UNMAPPED},
+ /*062*/ {"Elec Gtr 2", 27, SFX_UNMAPPED},
+ /*063*/ {"Sitar ", 104, SFX_UNMAPPED},
+ /*064*/ {"Acou Bass1", 32, SFX_UNMAPPED},
+ /*065*/ {"Acou Bass2", 33, SFX_UNMAPPED},
+ /*066*/ {"Elec Bass1", 34, SFX_UNMAPPED},
+ /*067*/ {"Elec Bass2", 39, SFX_UNMAPPED},
+ /*068*/ {"Slap Bass1", 36, SFX_UNMAPPED},
+ /*069*/ {"Slap Bass2", 37, SFX_UNMAPPED},
+ /*070*/ {"Fretless 1", 35, SFX_UNMAPPED},
+ /*071*/ {"Fretless 2", 35, SFX_UNMAPPED},
+ /*072*/ {"Flute 1 ", 73, SFX_UNMAPPED},
+ /*073*/ {"Flute 2 ", 73, SFX_UNMAPPED},
+ /*074*/ {"Piccolo 1 ", 72, SFX_UNMAPPED},
+ /*075*/ {"Piccolo 2 ", 72, SFX_UNMAPPED},
+ /*076*/ {"Recorder ", 74, SFX_UNMAPPED},
+ /*077*/ {"Panpipes ", 75, SFX_UNMAPPED},
+ /*078*/ {"Sax 1 ", 64, SFX_UNMAPPED},
+ /*079*/ {"Sax 2 ", 65, SFX_UNMAPPED},
+ /*080*/ {"Sax 3 ", 66, SFX_UNMAPPED},
+ /*081*/ {"Sax 4 ", 67, SFX_UNMAPPED},
+ /*082*/ {"Clarinet 1", 71, SFX_UNMAPPED},
+ /*083*/ {"Clarinet 2", 71, SFX_UNMAPPED},
+ /*084*/ {"Oboe ", 68, SFX_UNMAPPED},
+ /*085*/ {"Engl Horn ", 69, SFX_UNMAPPED},
+ /*086*/ {"Bassoon ", 70, SFX_UNMAPPED},
+ /*087*/ {"Harmonica ", 22, SFX_UNMAPPED},
+ /*088*/ {"Trumpet 1 ", 56, SFX_UNMAPPED},
+ /*089*/ {"Trumpet 2 ", 56, SFX_UNMAPPED},
+ /*090*/ {"Trombone 1", 57, SFX_UNMAPPED},
+ /*091*/ {"Trombone 2", 57, SFX_UNMAPPED},
+ /*092*/ {"Fr Horn 1 ", 60, SFX_UNMAPPED},
+ /*093*/ {"Fr Horn 2 ", 60, SFX_UNMAPPED},
+ /*094*/ {"Tuba ", 58, SFX_UNMAPPED},
+ /*095*/ {"Brs Sect 1", 61, SFX_UNMAPPED},
+ /*096*/ {"Brs Sect 2", 61, SFX_UNMAPPED},
+ /*097*/ {"Vibe 1 ", 11, SFX_UNMAPPED},
+ /*098*/ {"Vibe 2 ", 11, SFX_UNMAPPED},
+ /*099*/ {"Syn Mallet", 15, SFX_UNMAPPED},
+ /*100*/ {"Wind Bell ", 88, SFX_UNMAPPED},
+ /*101*/ {"Glock ", 9, SFX_UNMAPPED},
+ /*102*/ {"Tube Bell ", 14, SFX_UNMAPPED},
+ /*103*/ {"Xylophone ", 13, SFX_UNMAPPED},
+ /*104*/ {"Marimba ", 12, SFX_UNMAPPED},
+ /*105*/ {"Koto ", 107, SFX_UNMAPPED},
+ /*106*/ {"Sho ", 111, SFX_UNMAPPED},
+ /*107*/ {"Shakuhachi", 77, SFX_UNMAPPED},
+ /*108*/ {"Whistle 1 ", 78, SFX_UNMAPPED},
+ /*109*/ {"Whistle 2 ", 78, SFX_UNMAPPED},
+ /*110*/ {"BottleBlow", 76, SFX_UNMAPPED},
+ /*111*/ {"BreathPipe", 121, SFX_UNMAPPED},
+ /*112*/ {"Timpani ", 47, SFX_UNMAPPED},
+ /*113*/ {"MelodicTom", 117, SFX_UNMAPPED},
+ /*114*/ {"Deep Snare", SFX_MAPPED_TO_RHYTHM, 37},
+ /*115*/ {"Elec Perc1", 115, SFX_UNMAPPED}, /* ? */
+ /*116*/ {"Elec Perc2", 118, SFX_UNMAPPED}, /* ? */
+ /*117*/ {"Taiko ", 116, SFX_UNMAPPED},
+ /*118*/ {"Taiko Rim ", 118, SFX_UNMAPPED},
+ /*119*/ {"Cymbal ", SFX_MAPPED_TO_RHYTHM, 50},
+ /*120*/ {"Castanets ", SFX_MAPPED_TO_RHYTHM, SFX_UNMAPPED},
+ /*121*/ {"Triangle ", 112, SFX_UNMAPPED},
+ /*122*/ {"Orche Hit ", 55, SFX_UNMAPPED},
+ /*123*/ {"Telephone ", 124, SFX_UNMAPPED},
+ /*124*/ {"Bird Tweet", 123, SFX_UNMAPPED},
+ /*125*/ {"OneNoteJam", SFX_UNMAPPED, SFX_UNMAPPED}, /* ? */
+ /*126*/ {"WaterBells", 98, SFX_UNMAPPED},
+ /*127*/ {"JungleTune", SFX_UNMAPPED, SFX_UNMAPPED} /* ? */
+};
+
+static struct {
+ char *name;
+ gint8 gm_instr;
+ gint8 gm_rhythmkey;
+} MT32_RhythmTimbreMaps[] = {
+ /*00*/ {"Acou BD ", SFX_MAPPED_TO_RHYTHM, 34},
+ /*01*/ {"Acou SD ", SFX_MAPPED_TO_RHYTHM, 37},
+ /*02*/ {"Acou HiTom", 117, 49},
+ /*03*/ {"AcouMidTom", 117, 46},
+ /*04*/ {"AcouLowTom", 117, 40},
+ /*05*/ {"Elec SD ", SFX_MAPPED_TO_RHYTHM, 39},
+ /*06*/ {"Clsd HiHat", SFX_MAPPED_TO_RHYTHM, 41},
+ /*07*/ {"OpenHiHat1", SFX_MAPPED_TO_RHYTHM, 45},
+ /*08*/ {"Crash Cym ", SFX_MAPPED_TO_RHYTHM, 48},
+ /*09*/ {"Ride Cym ", SFX_MAPPED_TO_RHYTHM, 50},
+ /*10*/ {"Rim Shot ", SFX_MAPPED_TO_RHYTHM, 36},
+ /*11*/ {"Hand Clap ", SFX_MAPPED_TO_RHYTHM, 38},
+ /*12*/ {"Cowbell ", SFX_MAPPED_TO_RHYTHM, 55},
+ /*13*/ {"Mt HiConga", SFX_MAPPED_TO_RHYTHM, 61},
+ /*14*/ {"High Conga", SFX_MAPPED_TO_RHYTHM, 62},
+ /*15*/ {"Low Conga ", SFX_MAPPED_TO_RHYTHM, 63},
+ /*16*/ {"Hi Timbale", SFX_MAPPED_TO_RHYTHM, 64},
+ /*17*/ {"LowTimbale", SFX_MAPPED_TO_RHYTHM, 65},
+ /*18*/ {"High Bongo", SFX_MAPPED_TO_RHYTHM, 59},
+ /*19*/ {"Low Bongo ", SFX_MAPPED_TO_RHYTHM, 60},
+ /*20*/ {"High Agogo", 113, 66},
+ /*21*/ {"Low Agogo ", 113, 67},
+ /*22*/ {"Tambourine", SFX_MAPPED_TO_RHYTHM, 53},
+ /*23*/ {"Claves ", SFX_MAPPED_TO_RHYTHM, 74},
+ /*24*/ {"Maracas ", SFX_MAPPED_TO_RHYTHM, 69},
+ /*25*/ {"SmbaWhis L", 78, 71},
+ /*26*/ {"SmbaWhis S", 78, 70},
+ /*27*/ {"Cabasa ", SFX_MAPPED_TO_RHYTHM, 68},
+ /*28*/ {"Quijada ", SFX_MAPPED_TO_RHYTHM, 72},
+ /*29*/ {"OpenHiHat2", SFX_MAPPED_TO_RHYTHM, 43}
+};
+
+static gint8
+MT32_PresetRhythmKeymap[] = {
+ SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED,
+ SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED,
+ SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED,
+ SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, 34, 34, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, SFX_UNMAPPED, SFX_UNMAPPED, 53, SFX_UNMAPPED, 55, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, SFX_UNMAPPED, 74, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED,
+ SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED,
+ SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED,
+ SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED,
+ SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED,
+ SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED, SFX_UNMAPPED
+};
+
+/* +++ - Don't change unless you've got a good reason
+ ++ - Looks good, sounds ok
+ + - Not too bad, but is it right?
+ ? - Where do I map this one?
+ ?? - Any good ideas?
+ ??? - I'm clueless?
+ R - Rhythm... */
+static struct {
+ char *name;
+ gint8 gm_instr;
+ gint8 gm_rhythm_key;
+} MT32_MemoryTimbreMaps[] = {
+ {"AccPnoKA2 ", 1, SFX_UNMAPPED}, /* ++ (KQ1) */
+ {"Acou BD ", SFX_MAPPED_TO_RHYTHM, 34}, /* R (PQ2) */
+ {"Acou SD ", SFX_MAPPED_TO_RHYTHM, 37}, /* R (PQ2) */
+ {"AcouPnoKA ", 0, SFX_UNMAPPED}, /* ++ (KQ1) */
+ {"BASS ", 32, SFX_UNMAPPED}, /* + (LSL3) */
+ {"BASSOONPCM", 70, SFX_UNMAPPED}, /* + (CB) */
+ {"BEACH WAVE", 122, SFX_UNMAPPED}, /* + (LSL3) */
+ {"BagPipes ", 109, SFX_UNMAPPED},
+ {"BassPizzMS", 45, SFX_UNMAPPED}, /* ++ (HQ) */
+ {"BassoonKA ", 70, SFX_UNMAPPED}, /* ++ (KQ1) */
+ {"Bell MS", 112, SFX_UNMAPPED}, /* ++ (iceMan) */
+ {"Bells MS", 112, SFX_UNMAPPED}, /* + (HQ) */
+ {"Big Bell ", 14, SFX_UNMAPPED}, /* + (CB) */
+ {"Bird Tweet", 123, SFX_UNMAPPED},
+ {"BrsSect MS", 61, SFX_UNMAPPED}, /* +++ (iceMan) */
+ {"CLAPPING ", 126, SFX_UNMAPPED}, /* ++ (LSL3) */
+ {"Cabasa ", SFX_MAPPED_TO_RHYTHM, 68}, /* R (HBoG) */
+ {"Calliope ", 82, SFX_UNMAPPED}, /* +++ (HQ) */
+ {"CelticHarp", 46, SFX_UNMAPPED}, /* ++ (CoC) */
+ {"Chicago MS", 1, SFX_UNMAPPED}, /* ++ (iceMan) */
+ {"Chop ", 117, SFX_UNMAPPED},
+ {"Chorale MS", 52, SFX_UNMAPPED}, /* + (CoC) */
+ {"ClarinetMS", 71, SFX_UNMAPPED},
+ {"Claves ", SFX_MAPPED_TO_RHYTHM, 74}, /* R (PQ2) */
+ {"Claw MS", 118, SFX_UNMAPPED}, /* + (HQ) */
+ {"ClockBell ", 14, SFX_UNMAPPED}, /* + (CB) */
+ {"ConcertCym", SFX_MAPPED_TO_RHYTHM, 54}, /* R ? (KQ1) */
+ {"Conga MS", SFX_MAPPED_TO_RHYTHM, 63}, /* R (HQ) */
+ {"CoolPhone ", 124, SFX_UNMAPPED}, /* ++ (LSL3) */
+ {"CracklesMS", 115, SFX_UNMAPPED}, /* ? (CoC, HQ) */
+ {"CreakyD MS", SFX_UNMAPPED, SFX_UNMAPPED}, /* ??? (KQ1) */
+ {"Cricket ", 120, SFX_UNMAPPED}, /* ? (CB) */
+ {"CrshCymbMS", SFX_MAPPED_TO_RHYTHM, 56}, /* R +++ (iceMan) */
+ {"CstlGateMS", SFX_UNMAPPED, SFX_UNMAPPED}, /* ? (HQ) */
+ {"CymSwellMS", SFX_MAPPED_TO_RHYTHM, 54}, /* R ? (CoC, HQ) */
+ {"CymbRollKA", SFX_MAPPED_TO_RHYTHM, 56}, /* R ? (KQ1) */
+ {"Cymbal Lo ", SFX_UNMAPPED, SFX_UNMAPPED}, /* R ? (LSL3) */
+ {"card ", SFX_UNMAPPED, SFX_UNMAPPED}, /* ? (HBoG) */
+ {"DirtGtr MS", 30, SFX_UNMAPPED}, /* + (iceMan) */
+ {"DirtGtr2MS", 29, SFX_UNMAPPED}, /* + (iceMan) */
+ {"E Bass MS", 33, SFX_UNMAPPED}, /* + (SQ3) */
+ {"ElecBassMS", 33, SFX_UNMAPPED},
+ {"ElecGtr MS", 27, SFX_UNMAPPED}, /* ++ (iceMan) */
+ {"EnglHornMS", 69, SFX_UNMAPPED},
+ {"FantasiaKA", 88, SFX_UNMAPPED},
+ {"Fantasy ", 99, SFX_UNMAPPED}, /* + (PQ2) */
+ {"Fantasy2MS", 99, SFX_UNMAPPED}, /* ++ (CoC, HQ) */
+ {"Filter MS", 95, SFX_UNMAPPED}, /* +++ (iceMan) */
+ {"Filter2 MS", 95, SFX_UNMAPPED}, /* ++ (iceMan) */
+ {"Flame2 MS", 121, SFX_UNMAPPED}, /* ? (HQ) */
+ {"Flames MS", 121, SFX_UNMAPPED}, /* ? (HQ) */
+ {"Flute MS", 73, SFX_UNMAPPED}, /* +++ (HQ) */
+ {"FogHorn MS", 58, SFX_UNMAPPED},
+ {"FrHorn1 MS", 60, SFX_UNMAPPED}, /* +++ (HQ) */
+ {"FunnyTrmp ", 56, SFX_UNMAPPED}, /* ++ (CB) */
+ {"GameSnd MS", 80, SFX_UNMAPPED},
+ {"Glock MS", 9, SFX_UNMAPPED}, /* +++ (HQ) */
+ {"Gunshot ", 127, SFX_UNMAPPED}, /* +++ (CB) */
+ {"Hammer MS", SFX_UNMAPPED, SFX_UNMAPPED}, /* ? (HQ) */
+ {"Harmonica2", 22, SFX_UNMAPPED}, /* +++ (CB) */
+ {"Harpsi 1 ", 6, SFX_UNMAPPED}, /* + (HBoG) */
+ {"Harpsi 2 ", 6, SFX_UNMAPPED}, /* +++ (CB) */
+ {"Heart MS", 116, SFX_UNMAPPED}, /* ? (iceMan) */
+ {"Horse1 MS", 115, SFX_UNMAPPED}, /* ? (CoC, HQ) */
+ {"Horse2 MS", 115, SFX_UNMAPPED}, /* ? (CoC, HQ) */
+ {"InHale MS", 121, SFX_UNMAPPED}, /* ++ (iceMan) */
+ {"KNIFE ", 120, SFX_UNMAPPED}, /* ? (LSL3) */
+ {"KenBanjo ", 105, SFX_UNMAPPED}, /* +++ (CB) */
+ {"Kiss MS", 25, SFX_UNMAPPED}, /* ++ (HQ) */
+ {"KongHit ", SFX_UNMAPPED, SFX_UNMAPPED}, /* ??? (KQ1) */
+ {"Koto ", 107, SFX_UNMAPPED}, /* +++ (PQ2) */
+ {"Laser MS", 81, SFX_UNMAPPED}, /* ?? (HQ) */
+ {"Meeps MS", 62, SFX_UNMAPPED}, /* ? (HQ) */
+ {"MTrak MS", 62, SFX_UNMAPPED}, /* ?? (iceMan) */
+ {"MachGun MS", 127, SFX_UNMAPPED}, /* ? (iceMan) */
+ {"OCEANSOUND", 122, SFX_UNMAPPED}, /* + (LSL3) */
+ {"Oboe 2001 ", 68, SFX_UNMAPPED}, /* + (PQ2) */
+ {"Ocean MS", 122, SFX_UNMAPPED}, /* + (iceMan) */
+ {"PPG 2.3 MS", 75, SFX_UNMAPPED}, /* ? (iceMan) */
+ {"PianoCrank", SFX_UNMAPPED, SFX_UNMAPPED}, /* ? (CB) */
+ {"PicSnareMS", SFX_MAPPED_TO_RHYTHM, 39}, /* R ? (iceMan) */
+ {"PiccoloKA ", 72, SFX_UNMAPPED}, /* +++ (KQ1) */
+ {"PinkBassMS", 39, SFX_UNMAPPED},
+ {"Pizz2 ", 45, SFX_UNMAPPED}, /* ++ (CB) */
+ {"Portcullis", SFX_UNMAPPED, SFX_UNMAPPED}, /* ? (KQ1) */
+ {"Raspbry MS", 81, SFX_UNMAPPED}, /* ? (HQ) */
+ {"RatSqueek ", 72, SFX_UNMAPPED}, /* ? (CB, CoC) */
+ {"Record78 ", SFX_UNMAPPED, SFX_UNMAPPED}, /* +++ (CB) */
+ {"RecorderMS", 74, SFX_UNMAPPED}, /* +++ (CoC) */
+ {"Red Baron ", 125, SFX_UNMAPPED}, /* ? (CB) */
+ {"ReedPipMS ", 20, SFX_UNMAPPED}, /* +++ (Coc) */
+ {"RevCymb MS", 119, SFX_UNMAPPED},
+ {"RifleShot ", 127, SFX_UNMAPPED}, /* + (CB) */
+ {"RimShot MS", SFX_MAPPED_TO_RHYTHM, 36}, /* R */
+ {"SHOWER ", 52, SFX_UNMAPPED}, /* ? (LSL3) */
+ {"SQ Bass MS", 32, SFX_UNMAPPED}, /* + (SQ3) */
+ {"ShakuVibMS", 79, SFX_UNMAPPED}, /* + (iceMan) */
+ {"SlapBassMS", 36, SFX_UNMAPPED}, /* +++ (iceMan) */
+ {"Snare MS", SFX_MAPPED_TO_RHYTHM, 37}, /* R (HQ) */
+ {"Some Birds", 123, SFX_UNMAPPED}, /* + (CB) */
+ {"Sonar MS", 78, SFX_UNMAPPED}, /* ? (iceMan) */
+ {"Soundtrk2 ", 97, SFX_UNMAPPED}, /* +++ (CB) */
+ {"Soundtrack", 97, SFX_UNMAPPED}, /* ++ (CoC) */
+ {"SqurWaveMS", 80, SFX_UNMAPPED},
+ {"StabBassMS", 34, SFX_UNMAPPED}, /* + (iceMan) */
+ {"SteelDrmMS", 114, SFX_UNMAPPED}, /* +++ (iceMan) */
+ {"StrSect1MS", 48, SFX_UNMAPPED}, /* ++ (HQ) */
+ {"String MS", 45, SFX_UNMAPPED}, /* + (CoC) */
+ {"Syn-Choir ", 91, SFX_UNMAPPED},
+ {"Syn Brass4", 63, SFX_UNMAPPED}, /* ++ (PQ2) */
+ {"SynBass MS", 38, SFX_UNMAPPED},
+ {"SwmpBackgr", 120, SFX_UNMAPPED}, /* ?? (CB,HQ) */
+ {"T-Bone2 MS", 57, SFX_UNMAPPED}, /* +++ (HQ) */
+ {"Taiko ", 116, 34}, /* +++ (Coc) */
+ {"Taiko Rim ", 118, 36}, /* +++ (LSL3) */
+ {"Timpani1 ", 47, SFX_UNMAPPED}, /* +++ (CB) */
+ {"Tom MS", 117, 47}, /* +++ (iceMan) */
+ {"Toms MS", 117, 47}, /* +++ (CoC, HQ) */
+ {"Tpt1prtl ", 56, SFX_UNMAPPED}, /* +++ (KQ1) */
+ {"TriangleMS", 112, 80}, /* R (CoC) */
+ {"Trumpet 1 ", 56, SFX_UNMAPPED}, /* +++ (CoC) */
+ {"Type MS", 114, SFX_UNMAPPED}, /* ? (iceMan) */
+ {"WaterBells", 98, SFX_UNMAPPED}, /* + (PQ2) */
+ {"WaterFallK", SFX_UNMAPPED, SFX_UNMAPPED}, /* ? (KQ1) */
+ {"Whiporill ", 123, SFX_UNMAPPED}, /* + (CB) */
+ {"Wind ", SFX_UNMAPPED, SFX_UNMAPPED}, /* ? (CB) */
+ {"Wind MS", SFX_UNMAPPED, SFX_UNMAPPED}, /* ? (HQ, iceMan) */
+ {"Wind2 MS", SFX_UNMAPPED, SFX_UNMAPPED}, /* ? (CoC) */
+ {"Woodpecker", 115, SFX_UNMAPPED}, /* ? (CB) */
+ {"WtrFall MS", SFX_UNMAPPED, SFX_UNMAPPED}, /* ? (CoC, HQ, iceMan) */
+ {0, 0}
+};
+
+static gint8
+lookup_instrument(char *iname)
+{
+ int i = 0;
+
+ while (MT32_MemoryTimbreMaps[i].name) {
+ if (strncasecmp(iname, MT32_MemoryTimbreMaps[i].name, 10) == 0)
+ return MT32_MemoryTimbreMaps[i].gm_instr;
+ i++;
+ }
+ return SFX_UNMAPPED;
+}
+
+static gint8
+lookup_rhythm_key(char *iname)
+{
+ int i = 0;
+
+ while (MT32_MemoryTimbreMaps[i].name) {
+ if (strncasecmp(iname, MT32_MemoryTimbreMaps[i].name, 10) == 0)
+ return MT32_MemoryTimbreMaps[i].gm_rhythm_key;
+ i++;
+ }
+ return SFX_UNMAPPED;
+}
+
+static void
+print_map(int sci, int ins, int rhythm, int mt32)
+{
+#ifdef DEBUG_MT32_TO_GM
+ if (ins == SFX_UNMAPPED || (ins == SFX_MAPPED_TO_RHYTHM && rhythm == SFX_UNMAPPED)) {
+ sciprintf("[MT32-to-GM] No mapping available for [%i] `%s' (%i)\n",
+ sci, MT32_PresetTimbreMaps[mt32].name, mt32);
+ return;
+ }
+
+ if (ins == SFX_MAPPED_TO_RHYTHM) {
+ sciprintf("[MT32-to-GM] Mapping [%i] `%s' (%i) to `%s' [R] (%i)\n",
+ sci, MT32_PresetTimbreMaps[mt32].name, mt32,
+ GM_Percussion_Names[rhythm], rhythm);
+ return;
+ }
+
+ sciprintf("[MT32-to-GM] Mapping [%i] `%s' (%i) to `%s' (%i)\n",
+ sci, MT32_PresetTimbreMaps[mt32].name, mt32,
+ GM_Instrument_Names[ins], ins);
+#endif
+}
+
+static void
+print_map_mem(int sci, int ins, int rhythm, char *mt32)
+{
+#ifdef DEBUG_MT32_TO_GM
+ char name[11];
+
+ strncpy(name, mt32, 10);
+ name[10] = 0;
+
+ if (ins == SFX_UNMAPPED || (ins == SFX_MAPPED_TO_RHYTHM && rhythm == SFX_UNMAPPED)) {
+ sciprintf("[MT32-to-GM] No mapping available for [%i] `%s'\n",
+ sci, name);
+ return;
+ }
+
+ if (ins == SFX_MAPPED_TO_RHYTHM) {
+ sciprintf("[MT32-to-GM] Mapping [%i] `%s' to `%s' [R] (%i)\n",
+ sci, name, GM_Percussion_Names[rhythm], rhythm);
+ return;
+ }
+
+ sciprintf("[MT32-to-GM] Mapping [%i] `%s' to `%s' (%i)\n",
+ sci, name, GM_Instrument_Names[ins], ins);
+#endif
+}
+
+static void
+print_map_rhythm(int sci, int ins, int rhythm, int mt32)
+{
+#ifdef DEBUG_MT32_TO_GM
+ if (ins == SFX_UNMAPPED || (ins == SFX_MAPPED_TO_RHYTHM && rhythm == SFX_UNMAPPED)) {
+ sciprintf("[MT32-to-GM] No mapping available for [%i] `%s' [R] (%i)\n",
+ sci, MT32_RhythmTimbreMaps[mt32].name, mt32);
+ return;
+ }
+
+ if (ins == SFX_MAPPED_TO_RHYTHM) {
+ sciprintf("[MT32-to-GM] Mapping [%i] `%s' [R] (%i) to `%s' [R] (%i)\n",
+ sci, MT32_RhythmTimbreMaps[mt32].name, mt32,
+ GM_Percussion_Names[rhythm], rhythm);
+ return;
+ }
+
+ sciprintf("[MT32-to-GM] Mapping [%i] `%s' [R] (%i) to `%s' (%i)\n",
+ sci, MT32_RhythmTimbreMaps[mt32].name, mt32,
+ GM_Instrument_Names[ins], ins);
+#endif
+}
+
+static void
+print_map_rhythm_mem(int sci, int rhythm, char *mt32)
+{
+#ifdef DEBUG_MT32_TO_GM
+ char name[11];
+
+ strncpy(name, mt32, 10);
+ name[10] = 0;
+
+ if (rhythm == SFX_UNMAPPED) {
+ sciprintf("[MT32-to-GM] No mapping available for [%i] `%s'\n",
+ sci, name);
+ return;
+ }
+
+ sciprintf("[MT32-to-GM] Mapping [%i] `%s' to `%s' (%i)\n",
+ sci, name, GM_Percussion_Names[rhythm], rhythm);
+#endif
+}
+
+sfx_instrument_map_t *
+sfx_instrument_map_mt32_to_gm(byte *data, size_t size)
+{
+ int memtimbres, patches;
+ guint8 group, number, keyshift, finetune, bender_range;
+ guint8 *patchpointer;
+ guint32 pos;
+ sfx_instrument_map_t * map;
+ int i;
+ int type;
+
+ map = sfx_instrument_map_new(0);
+
+ for (i = 0; i < SFX_INSTRUMENTS_NR; i++) {
+ map->patch_map[i].patch = MT32_PresetTimbreMaps[i].gm_instr;
+ map->patch_key_shift[i] = 0;
+ map->patch_volume_adjust[i] = 0;
+ map->patch_bend_range[i] = 12;
+ map->velocity_map_index[i] = SFX_NO_VELOCITY_MAP;
+ }
+
+ map->percussion_volume_adjust = 0;
+ map->percussion_velocity_map_index = SFX_NO_VELOCITY_MAP;
+
+ for (i = 0; i < SFX_RHYTHM_NR; i++) {
+ map->percussion_map[i] = MT32_PresetRhythmKeymap[i];
+ map->percussion_velocity_scale[i] = SFX_MAX_VELOCITY;
+ }
+
+ if (!data) {
+ sciprintf("[MT32-to-GM] No MT-32 patch data supplied, using default mapping\n");
+ return map;
+ }
+
+ type = sfx_instrument_map_detect(data, size);
+
+ if (type == SFX_MAP_UNKNOWN) {
+ sciprintf("[MT32-to-GM] Patch data format unknown, using default mapping\n");
+ return map;
+ }
+ if (type == SFX_MAP_MT32_GM) {
+ sciprintf("[MT32-to-GM] Patch data format not supported, using default mapping\n");
+ return map;
+ }
+
+ memtimbres = *(data + 0x1EB);
+ pos = 0x1EC + memtimbres * 0xF6;
+
+ if (size > pos && ((0x100 * *(data + pos) + *(data +pos + 1)) == 0xABCD)) {
+ patches = 96;
+ pos += 2 + 8 * 48;
+ } else
+ patches = 48;
+
+ sciprintf("[MT32-to-GM] %d MT-32 Patches detected\n", patches);
+ sciprintf("[MT32-to-GM] %d MT-32 Memory Timbres\n", memtimbres);
+
+ sciprintf("[MT32-to-GM] Mapping patches..\n");
+
+ for (i = 0; i < patches; i++) {
+ char *name;
+
+ if (i < 48)
+ patchpointer = data + 0x6B + 8 * i;
+ else
+ patchpointer = data + 0x1EC + 8 * (i - 48) + memtimbres * 0xF6 + 2;
+
+ group = *patchpointer;
+ number = *(patchpointer + 1);
+ keyshift = *(patchpointer + 2);
+ finetune = *(patchpointer + 3);
+ bender_range = *(patchpointer + 4);
+
+ switch (group) {
+ case 0:
+ map->patch_map[i].patch = MT32_PresetTimbreMaps[number].gm_instr;
+ map->patch_map[i].rhythm = MT32_PresetTimbreMaps[number].gm_rhythm_key;
+ print_map(i, map->patch_map[i].patch, map->patch_map[i].rhythm, number);
+ break;
+ case 1:
+ map->patch_map[i].patch = MT32_PresetTimbreMaps[number + 64].gm_instr;
+ map->patch_map[i].rhythm = MT32_PresetTimbreMaps[number + 64].gm_rhythm_key;
+ print_map(i, map->patch_map[i].patch, map->patch_map[i].rhythm, number + 64);
+ break;
+ case 2:
+ name = (char *) data + 0x1EC + number * 0xF6;
+ map->patch_map[i].patch = lookup_instrument(name);
+ map->patch_map[i].rhythm = SFX_UNMAPPED;
+ print_map_mem(i, map->patch_map[i].patch, map->patch_map[i].rhythm, name);
+ break;
+ case 3:
+ map->patch_map[i].patch = MT32_RhythmTimbreMaps[number].gm_instr;
+ map->patch_map[i].rhythm = SFX_UNMAPPED;
+ print_map_rhythm(i, map->patch_map[i].patch, map->patch_map[i].rhythm, number);
+ break;
+ default:
+ break;
+ }
+
+ /* map->patch_key_shift[i] = (int) (keyshift & 0x3F) - 24; */
+ map->patch_bend_range[i] = bender_range & 0x1F;
+ }
+
+ if (size > pos && ((0x100 * *(data + pos) + *(data + pos + 1)) == 0xDCBA)) {
+ sciprintf("[MT32-to-GM] Mapping percussion..\n");
+
+ for (i = 0; i < 64 ; i++) {
+ number = *(data + pos + 4 * i + 2);
+
+ if (number < 64) {
+ char *name = (char *) data + 0x1EC + number * 0xF6;
+ map->percussion_map[i + 23] = lookup_rhythm_key(name);
+ print_map_rhythm_mem(i, map->percussion_map[i + 23], name);
+ } else {
+ if (number < 94) {
+ map->percussion_map[i + 23] = MT32_RhythmTimbreMaps[number - 64].gm_rhythmkey;
+ print_map_rhythm(i, SFX_MAPPED_TO_RHYTHM, map->percussion_map[i + 23], number - 64);
+ } else
+ map->percussion_map[i + 23] = SFX_UNMAPPED;
+ }
+
+ map->percussion_velocity_scale[i + 23] = *(data + pos + 4 * i + 3) * SFX_MAX_VELOCITY / 100;
+ }
+ }
+
+ return map;
+}
+
diff --git a/engines/sci/sfx/seq/mt32.c b/engines/sci/sfx/seq/mt32.c
new file mode 100644
index 0000000000..b71d474927
--- /dev/null
+++ b/engines/sci/sfx/seq/mt32.c
@@ -0,0 +1,480 @@
+/***************************************************************************
+ midi_mt32.c Copyright (C) 2000,2001 Rickard Lind, Solomon Peachy
+ mt32.c Copyright (C) 2002..04 Christoph Reichenbach
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include "../sequencer.h"
+#include "instrument-map.h"
+#include <resource.h>
+
+#ifdef _WIN32
+# include <win32/sci_win32.h>
+# include <windows.h>
+#endif
+
+#ifdef __BEOS__
+# include <be/kernel/OS.h>
+#endif
+
+static int delta = 0; /* Accumulated delta time */
+static midi_writer_t *midi_writer = NULL;
+
+static int midi_mt32_poke(guint32 address, guint8 *data, unsigned int n);
+static int midi_mt32_poke_gather(guint32 address, guint8 *data1, unsigned int count1,
+ guint8 *data2, unsigned int count2);
+static int midi_mt32_write_block(guint8 *data, unsigned int count);
+static int midi_mt32_sysex_delay(void);
+static int midi_mt32_volume(guint8 volume);
+static int midi_mt32_reverb(int param);
+static int midi_mt32_event(byte command, int argc, byte *argv);
+static int midi_mt32_allstop(void);
+
+static int type;
+static guint8 sysex_buffer[266] = {0xF0, 0x41, 0x10, 0x16, 0x12};
+static guint8 default_reverb;
+static char shutdown_msg[20];
+
+static long mt32_init_sec, mt32_init_usec; /* Time at initialisation */
+static int mt32_init_delay = 0; /* Used to count the number of ticks (1/60s of a second) we should
+ ** wait before initialisation has been completed */
+
+/* timbre, volume, panpot, reverb. keys 24-87 (64 keys)*/
+static guint8 default_rhythm_keymap[256] = { /* MT-32 default */
+ 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1, /* 24-27 */
+ 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1,
+ 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x40,0x64,7,1,
+ 0x40,0x64,7,1, 0x4a,0x64,6,1, 0x41,0x64,7,1, 0x4b,0x64,8,1,
+ 0x45,0x64,6,1, 0x44,0x64,11,1, 0x46,0x64,6,1, 0x44,0x64,11,1,
+ 0x5d,0x64,6,1, 0x43,0x64,8,1, 0x47,0x64,6,1, 0x43,0x64,8,1,
+ 0x42,0x64,3,1, 0x48,0x64,6,1, 0x42,0x64,3,1, 0x49,0x64,8,1,
+ 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x56,0x64,9,1, 0x7f,0x64,7,1,
+ 0x4c,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1,
+ 0x52,0x64,2,1, 0x53,0x64,4,1, 0x4d,0x64,8,1, 0x4e,0x64,9,1,
+ 0x4f,0x64,10,1, 0x50,0x64,7,1, 0x51,0x64,5,1, 0x54,0x64,2,1,
+ 0x55,0x64,2,1, 0x5b,0x64,9,1, 0x58,0x64,4,1, 0x5a,0x64,9,1,
+ 0x59,0x64,9,1, 0x5c,0x64,10,1, 0x7f,0x64,7,1, 0x57,0x64,12,1,
+ 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1,
+ 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1,
+ 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1, 0x7f,0x64,7,1 /* 84-87 */
+};
+
+static guint8 default_partial_reserve[9] = { /* MT-32 DEFAULT */
+ 3, 10, 6, 4, 3, 0, 0, 0, 6 };
+
+static struct {
+ guint8 mode;
+ guint8 time;
+ guint8 level;
+} mt32_reverb[11];
+
+
+static int
+midiout_write_block(byte *buf, int len, int delta)
+{
+ if (delta)
+ midi_writer->delay(midi_writer, delta);
+
+ return midi_writer->write(midi_writer, buf, len);
+}
+
+/* The following is the result of some experimenting, trying to approach the MT32's processing speed */
+#define MAGIC_MIDIOUT_DELAY 40
+
+static int
+midiout_write_delayed_block(byte *buf, int len)
+ /* Only used for initial programming */
+{
+ int rv = midiout_write_block(buf, len, 0);
+ int delay = 1 + (len / MAGIC_MIDIOUT_DELAY);
+
+ midi_writer->delay(midi_writer, delay);
+
+ mt32_init_delay += delay; /* Keep track of delay times */
+
+ return rv;
+}
+
+/* send default rhythm map and reserve */
+int midi_mt32_defaults(guint8 volume, guint8 reverb) {
+ printf("MT-32: Writing Default Rhythm key map\n");
+ midi_mt32_poke(0x030110, default_rhythm_keymap, 256);
+
+ printf("MT-32: Writing Default Partial Reserve\n");
+ midi_mt32_poke(0x100004, default_partial_reserve, 9);
+
+ if (reverb) {
+ mt32_reverb[0].mode = 0;
+ mt32_reverb[0].time = 5;
+ mt32_reverb[0].level = 3;
+ default_reverb = 0;
+
+ printf("MT-32: Setting up default reverb levels\n");
+ midi_mt32_reverb(default_reverb);
+ }
+
+ if (volume) {
+ printf("MT-32: Setting default volume (%d)\n", volume);
+ midi_mt32_volume(volume);
+ }
+
+ return SFX_OK;
+}
+
+int midi_mt32_open(int length, byte *data, int length2, byte *data2, void *dev)
+{
+ guint8 unknown_sysex[6] = {0x16, 0x16, 0x16, 0x16, 0x16, 0x16};
+ guint8 i, memtimbres;
+ unsigned int block2, block3;
+
+ if (!dev) {
+ fprintf(stderr, "Attempt to use MT-32 sequencer without device\n");
+ return SFX_ERROR;
+ }
+
+ sci_gettime(&mt32_init_sec, &mt32_init_usec);
+
+ midi_writer = (midi_writer_t *) dev;
+
+ midi_mt32_allstop();
+
+ if (!data) {
+ type = SFX_MAP_UNKNOWN;
+ sciprintf("MT-32: No patch.001 found, using defaults\n");
+ } else {
+ type = sfx_instrument_map_detect(data, length);
+ if (type == SFX_MAP_UNKNOWN)
+ sciprintf("MT-32: Unknown patch.001 format, using defaults\n");
+ else
+ sciprintf("MT-32: Programming Roland MT-32 with patch.001 (v%i) %d bytes\n", type, length);
+ }
+
+ if (type == SFX_MAP_MT32) {
+ /* Display MT-32 initialization message */
+ printf("MT-32: Displaying Text: \"%.20s\"\n", data + 20);
+ midi_mt32_poke(0x200000, data + 20, 20);
+
+ /* Cache a copy of the shutdown message */
+ memcpy(shutdown_msg, data + 40, 20);
+
+ /* Write Patches (48 or 96) */
+ memtimbres = data[491];
+ block2 = (memtimbres * 246) + 492;
+ printf("MT-32: Writing Patches #01 - #32\n");
+ midi_mt32_poke(0x050000, data + 107, 256);
+ if ((length > block2) &&
+ data[block2] == 0xAB &&
+ data[block2 + 1] == 0xCD) {
+ printf("MT-32: Writing Patches #33 - #64\n");
+ midi_mt32_poke_gather(0x050200, data + 363, 128, data + block2 + 2, 128);
+ printf("MT-32: Writing Patches #65 - #96\n");
+ midi_mt32_poke(0x050400, data + block2 + 130, 256);
+ block3 = block2 + 386;
+ } else {
+ printf("MT-32: Writing Patches #33 - #48\n");
+ midi_mt32_poke(0x050200, data + 363, 128);
+ block3 = block2;
+ }
+ /* Write Memory Timbres */
+ for (i = 0; i < memtimbres; i++) {
+ printf("MT-32: Writing Memory Timbre #%02d: \"%.10s\"\n",
+ i + 1, data + 492 + i * 246);
+ midi_mt32_poke(0x080000 + (i << 9), data + 492 + i * 246, 246);
+ }
+ /* Write Rhythm key map and Partial Reserve */
+ if ((length > block3) &&
+ data[block3] == 0xDC &&
+ data[block3 + 1] == 0xBA) {
+ printf("MT-32: Writing Rhythm key map\n");
+ midi_mt32_poke(0x030110, data + block3 + 2, 256);
+ printf("MT-32: Writing Partial Reserve\n");
+ midi_mt32_poke(0x100004, data + block3 + 258, 9);
+ } else {
+ midi_mt32_defaults(0,0); /* send default keymap/reserve */
+ }
+ /* Display MT-32 initialization done message */
+ printf("MT-32: Displaying Text: \"%.20s\"\n", data);
+ midi_mt32_poke(0x200000, data, 20);
+ /* Write undocumented MT-32(?) SysEx */
+ printf("MT-32: Writing {F0 41 10 16 12 52 00 0A 16 16 16 16 16 16 20 F7}\n");
+ midi_mt32_poke(0x52000A, unknown_sysex, 6);
+ printf("MT-32: Setting up reverb levels\n");
+ default_reverb = data[0x3e];
+ memcpy(mt32_reverb,data+ 0x4a, 3 * 11);
+ midi_mt32_reverb(default_reverb);
+ printf("MT-32: Setting default volume (%d)\n", data[0x3c]);
+ midi_mt32_volume(data[0x3c]);
+ return 0;
+ } else if (type == SFX_MAP_MT32_GM) {
+ printf("MT-32: Loading SysEx bank\n");
+ midi_mt32_write_block(data + 1155, (data[1154] << 8) + data[1153]);
+ return 0;
+ } else {
+ midi_mt32_poke(0x200000, (guint8 *)" FreeSCI Rocks! ", 20);
+ return midi_mt32_defaults(0x0c,1); /* send defaults in absence of patch data */
+ }
+ return -1;
+}
+
+int midi_mt32_close(void)
+{
+ midi_mt32_allstop();
+ if (type == 0) {
+ printf("MT-32: Displaying Text: \"%.20s\"\n", shutdown_msg);
+ midi_mt32_poke(0x200000, (unsigned char *) shutdown_msg, 20);
+ }
+ midi_writer->close(midi_writer);
+ return SFX_OK;
+}
+
+int midi_mt32_volume(guint8 volume)
+{
+ volume &= 0x7f; /* (make sure it's not over 127) */
+ if (midi_mt32_poke(0x100016, &volume, 1) < 0)
+ return -1;
+
+ return 0;
+}
+
+int midi_mt32_allstop(void)
+{
+ byte buf[4];
+ int i;
+
+ buf[0] = 0x7b;
+ buf[1] = 0;
+ buf[2] = 0;
+
+ for (i = 0; i < 16; i++) {
+ midi_mt32_event((guint8)(0xb0 | i), 2, buf);
+ }
+
+ return 0;
+}
+
+int midi_mt32_reverb(int param)
+{
+ guint8 buffer[3];
+
+ if (param == -1)
+ param = default_reverb;
+
+ printf("MT-32: Sending reverb # %d (%d, %d, %d)\n",param, mt32_reverb[param].mode,
+ mt32_reverb[param].time,
+ mt32_reverb[param].level);
+
+ buffer[0] = mt32_reverb[param].mode;
+ buffer[1] = mt32_reverb[param].time;
+ buffer[2] = mt32_reverb[param].level;
+ midi_mt32_poke(0x100001, buffer, 3);
+
+ return 0;
+}
+
+
+static int
+midi_mt32_poke(guint32 address, guint8 *data, unsigned int count)
+{
+ guint8 checksum = 0;
+ unsigned int i;
+
+ if (count > 256) return -1;
+
+ checksum -= (sysex_buffer[5] = (char)((address >> 16) & 0x7F));
+ checksum -= (sysex_buffer[6] = (char)((address >> 8) & 0x7F));
+ checksum -= (sysex_buffer[7] = (char)(address & 0x7F));
+
+ for (i = 0; i < count; i++)
+ checksum -= (sysex_buffer[i + 8] = data[i]);
+
+ sysex_buffer[count + 8] = checksum & 0x7F;
+ sysex_buffer[count + 9] = 0xF7;
+
+ midiout_write_delayed_block(sysex_buffer, count + 10);
+ if (midi_writer->flush)
+ midi_writer->flush(midi_writer);
+ midi_mt32_sysex_delay();
+
+ return count + 10;
+
+}
+
+static int
+midi_mt32_poke_gather(guint32 address, guint8 *data1, unsigned int count1,
+ guint8 *data2, unsigned int count2)
+{
+ guint8 checksum = 0;
+ unsigned int i;
+
+ if ((count1 + count2) > 256) return -1;
+
+ checksum -= (sysex_buffer[5] = (char)((address >> 16) & 0x7F));
+ checksum -= (sysex_buffer[6] = (char)((address >> 8) & 0x7F));
+ checksum -= (sysex_buffer[7] = (char)(address & 0x7F));
+
+ for (i = 0; i < count1; i++)
+ checksum -= (sysex_buffer[i + 8] = data1[i]);
+ for (i = 0; i < count2; i++)
+ checksum -= (sysex_buffer[i + 8 + count1] = data2[i]);
+
+ sysex_buffer[count1 + count2 + 8] = checksum & 0x7F;
+ sysex_buffer[count1 + count2 + 9] = 0xF7;
+
+ midiout_write_delayed_block(sysex_buffer, count1 + count2 + 10);
+ if (midi_writer->flush)
+ midi_writer->flush(midi_writer);
+ midi_mt32_sysex_delay();
+ return count1 + count2 + 10;
+}
+
+
+static int
+midi_mt32_write_block(guint8 *data, unsigned int count)
+{
+ unsigned int block_start = 0;
+ unsigned int i = 0;
+
+ while (i < count) {
+ if ((data[i] == 0xF0) && (i != block_start)) {
+ midiout_write_delayed_block(data + block_start, i - block_start);
+ block_start = i;
+ }
+ if (data[i] == 0xF7) {
+ midiout_write_delayed_block(data + block_start, i - block_start + 1);
+ midi_mt32_sysex_delay();
+ block_start = i + 1;
+ }
+ i++;
+ }
+ if (count >= block_start) {
+ if (midiout_write_delayed_block(data + block_start, count - block_start
+ ) != (count - block_start)) {
+ fprintf(stderr, "midi_mt32_write_block(): midiout_write_block failed!\n");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+midi_mt32_sysex_delay(void)
+{
+ /* Under Win32, we won't get any sound, in any case... */
+#ifdef HAVE_USLEEP
+ usleep(320 * 63); /* One MIDI byte is 320us, 320us * 63 > 20ms */
+#elif defined (_WIN32)
+ Sleep(((320 * 63) / 1000) + 1);
+#elif defined (__BEOS__)
+ snooze(320 * 63);
+#else
+ sleep(1);
+#endif
+ return 0;
+}
+
+static int
+midi_mt32_event(byte command, int argc, byte *argv)
+{
+ byte buf[8];
+
+ buf[0] = command;
+ memcpy(buf + 1, argv, argc);
+
+ midiout_write_block(buf, argc + 1, delta);
+ delta = 0;
+
+ return SFX_OK;
+}
+
+
+static void
+delay_init(void)
+{/* Wait for MT-32 initialisation to complete */
+ long endsec = mt32_init_sec, uendsec = mt32_init_usec;
+ long sec, usec;
+ int loopcount = 0;
+
+ uendsec += (mt32_init_delay * 100000) / 6; /* mt32_init_delay is in ticks (1/60th seconds), uendsecs in microseconds */
+ endsec += uendsec / 1000000;
+ uendsec %= 1000000;
+
+
+ do {
+ if (loopcount == 1)
+ sciprintf("Waiting for MT-32 programming to complete...\n");
+
+ sci_gettime(&sec, &usec);
+ sleep(1); /* Idle a bit */
+ ++loopcount;
+ } while ((sec < endsec) || ((sec == endsec) && (usec < uendsec)));
+
+}
+
+static int
+midi_mt32_reset_timer(GTimeVal ts)
+{
+ if (mt32_init_delay) { /* We might still have to wait for initialisation to complete */
+ delay_init();
+ mt32_init_delay = 0;
+ }
+
+
+ midi_writer->reset_timer(midi_writer);
+ return SFX_OK;
+}
+
+
+static int
+midi_mt32_delay(int ticks)
+{
+ delta += ticks; /* Accumulate, write before next command */
+ return SFX_OK;
+}
+
+static int
+midi_mt32_set_option(char *name, char *value)
+{
+ return SFX_ERROR; /* No options are supported at this time */
+}
+
+/* the driver struct */
+
+sfx_sequencer_t sfx_sequencer_mt32 = {
+ "MT32",
+ "0.1",
+ SFX_DEVICE_MIDI, /* No device dependancy-- fixme, this might becomde ossseq */
+ &midi_mt32_set_option,
+ &midi_mt32_open,
+ &midi_mt32_close,
+ &midi_mt32_event,
+ &midi_mt32_delay,
+ &midi_mt32_reset_timer,
+ &midi_mt32_allstop,
+ &midi_mt32_volume,
+ &midi_mt32_reverb,
+ 001, /* patch.001 */
+ SFX_SEQ_PATCHFILE_NONE,
+ 0x01, /* playflag */
+ 1, /* do play channel 9 */
+ 32, /* Max polyphony */
+ 0 /* Does not require any write-ahead by its own */
+};
diff --git a/engines/sci/sfx/seq/oss-adlib.c b/engines/sci/sfx/seq/oss-adlib.c
new file mode 100644
index 0000000000..0406556f56
--- /dev/null
+++ b/engines/sci/sfx/seq/oss-adlib.c
@@ -0,0 +1,374 @@
+/***************************************************************************
+ oss-adlib.c Copyright (C) 2001 Solomon Peachy, 03,04 Christoph Reichenbach
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include "../sequencer.h"
+
+#ifdef HAVE_SYS_SOUNDCARD_H
+
+#include "../adlib.h"
+
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/soundcard.h>
+#include <sfx_iterator.h> /* for some MIDI information */
+
+#if 1
+SEQ_DEFINEBUF(2048);
+static int seqfd;
+#else
+extern unsigned char _seqbuf[2048];
+extern int _seqbuflen;
+extern int _seqbufptr;
+extern int seqfd;
+#endif
+
+static guint8 instr[MIDI_CHANNELS];
+static int dev;
+static int free_voices = ADLIB_VOICES;
+static long note_time[ADLIB_VOICES];
+static unsigned char oper_note[ADLIB_VOICES];
+static unsigned char oper_chn[ADLIB_VOICES];
+
+#if 1
+void seqbuf_dump(void) /* OSS upcall */
+{
+ if (_seqbufptr)
+ if (write(seqfd, _seqbuf, _seqbufptr) == -1) {
+ perror("ADLIB write ");
+ exit(-1);
+ }
+ _seqbufptr = 0;
+}
+#endif
+
+/* initialise note/operator lists, etc. */
+void adlib_init_lists(void)
+{
+ int i;
+ for(i = 0 ; i < ADLIB_VOICES ; i++) {
+ oper_note[i] = 255;
+ oper_chn[i] = 255;
+ note_time[i] = 0;
+ }
+ free_voices = ADLIB_VOICES;
+}
+
+int adlib_stop_note(int chn, int note, int velocity)
+{
+ int i, op=255;
+
+ for (i=0;i<ADLIB_VOICES && op==255;i++) {
+ if (oper_chn[i] == chn)
+ if (oper_note[i] == note)
+ op=i;
+ }
+
+ if (op==255) {
+ printf ("can't stop.. chn %d %d %d\n", chn, note, velocity);
+ return 255; /* not playing */
+ }
+
+ SEQ_STOP_NOTE(dev, op, note, velocity);
+ SEQ_DUMPBUF();
+
+ oper_chn[op] = 255;
+ oper_note[op] = 255;
+ note_time[op] = 0;
+
+ free_voices++;
+
+ return op;
+}
+
+int adlib_kill_one_note(int chn)
+{
+ int oldest = 255, i = 255;
+ long time = 0;
+
+ if (free_voices >= ADLIB_VOICES) {
+ printf("Free list empty but no notes playing\n");
+ return 255;
+ } /* No notes playing */
+
+ for (i = 0; i < ADLIB_VOICES ; i++) {
+ if (oper_chn[i] != chn)
+ continue;
+ if (note_time[i] == 0)
+ continue;
+ if (time == 0) {
+ time = note_time[i];
+ oldest = i;
+ continue;
+ }
+ if (note_time[i] < time) {
+ time = note_time[i];
+ oldest = i;
+ }
+ }
+
+ /* printf("Killing chn %d, oper %d\n", chn, oldest); */
+
+ if (oldest == 255)
+ return 255; /* Was already stopped. Why? */
+
+ SEQ_STOP_NOTE(dev, oldest, oper_note[oldest], 0);
+ SEQ_DUMPBUF();
+
+ oper_chn[oldest] = 255;
+ oper_note[oldest] = 255;
+ note_time[oldest] = 0;
+ free_voices++;
+
+ return oldest;
+}
+
+static void
+adlib_start_note(int chn, int note, int velocity)
+{
+ int free;
+ struct timeval now;
+
+ if (velocity == 0) {
+ adlib_stop_note(chn, note, velocity);
+ return;
+ }
+
+ gettimeofday(&now, NULL);
+
+ if (free_voices <= 0)
+ free = adlib_kill_one_note(chn);
+ else
+ for (free = 0; free < ADLIB_VOICES ; free++)
+ if (oper_chn[free] == 255)
+ break;
+
+ /* printf("play operator %d/%d: %d %d %d\n", free, free_voices, chn, note, velocity); */
+
+ oper_chn[free] = chn;
+ oper_note[free] = note;
+ note_time[free] = now.tv_sec * 1000000 + now.tv_usec;
+ free_voices--;
+
+ SEQ_SET_PATCH(dev, free, instr[chn]);
+ SEQ_START_NOTE(dev, free, note, velocity);
+ SEQ_DUMPBUF();
+}
+
+static int
+midi_adlib_open(int data_length, byte *data_ptr, int data2_length,
+ byte *data2_ptr, void *seq)
+{
+ int nrdevs, i, n;
+ struct synth_info info;
+ struct sbi_instrument sbi;
+
+ if (data_length < 1344) {
+ printf ("invalid patch.003");
+ return -1;
+ }
+
+ for (i = 0; i < 48; i++)
+ make_sbi((adlib_def *)(data_ptr+(28 * i)), adlib_sbi[i]);
+
+ if (data_length > 1344)
+ for (i = 48; i < 96; i++)
+ make_sbi((adlib_def *)(data_ptr+2+(28 * i)), adlib_sbi[i]);
+
+ memset(instr, 0, sizeof(instr));
+
+ if (!IS_VALID_FD(seqfd=open("/dev/sequencer", O_WRONLY, 0))) {
+ perror("/dev/sequencer");
+ return(-1);
+ }
+ if (ioctl(seqfd, SNDCTL_SEQ_NRSYNTHS, &nrdevs) == -1) {
+ perror("/dev/sequencer");
+ return(-1);
+ }
+ for (i=0;i<nrdevs && dev==-1;i++) {
+ info.device = i;
+ if (ioctl(seqfd, SNDCTL_SYNTH_INFO, &info)==-1) {
+ perror("info: /dev/sequencer");
+ return(-1);
+ }
+ if (info.synth_type == SYNTH_TYPE_FM)
+ dev = i;
+ }
+ if (dev == -1) {
+ fprintf(stderr, "ADLIB: FM synthesizer not detected\n");
+ return(-1);
+ }
+
+ /* free_voices = info.nr_voices; */
+ adlib_init_lists();
+
+ printf("ADLIB: Loading patches into synthesizer\n");
+ sbi.device = dev;
+ sbi.key = FM_PATCH;
+ for (i = 0; i < 96; i++) {
+ for (n = 0; n < 32; n++)
+ memcpy(sbi.operators, &adlib_sbi[i], sizeof(sbi_instr_data));
+ sbi.channel=i;
+ SEQ_WRPATCH(&sbi, sizeof(sbi));
+ SEQ_DUMPBUF();
+ }
+ SEQ_START_TIMER();
+ SEQ_SET_TEMPO(60);
+ SEQ_DUMPBUF();
+ return 0;
+}
+
+
+static int
+midi_adlib_close(void)
+{
+ SEQ_DUMPBUF();
+ return close(seqfd);
+}
+
+
+static int
+midi_adlib_allstop(void)
+{
+ int i;
+ for (i = 0; i < ADLIB_VOICES ; i++) {
+ if (oper_chn[i] == 255)
+ continue;
+ adlib_stop_note(oper_chn[i], oper_note[i], 0);
+ }
+ adlib_init_lists();
+
+ return 0;
+}
+
+static int
+midi_adlib_reverb(int param)
+{
+ printf("reverb NYI %04x \n", param);
+ return 0;
+}
+
+static inline int
+midi_adlib_event1(guint8 command, guint8 note, guint8 velocity)
+{
+ guint8 channel, oper;
+
+ channel = command & 0x0f;
+ oper = command & 0xf0;
+
+ switch (oper) {
+ case 0x80:
+ adlib_stop_note(channel, note, velocity);
+ return 0;
+ case 0x90:
+ adlib_start_note(channel,note,velocity);
+ return 0;
+ case 0xe0: /* Pitch bend needs scaling? */
+ SEQ_BENDER(dev, channel, ((note << 8) & velocity));
+ SEQ_DUMPBUF();
+ break;
+ case 0xb0: /* CC changes. we ignore. */
+ /* XXXX we need to parse out 0x07 volume, at least. */
+ return 0;
+ case 0xd0: /* aftertouch */
+ SEQ_CHN_PRESSURE(dev, channel, note);
+ SEQ_DUMPBUF();
+ return 0;
+ default:
+ printf("ADLIB: Unknown event %02x\n", command);
+ return 0;
+ }
+
+ SEQ_DUMPBUF();
+ return 0;
+}
+
+static inline int
+midi_adlib_event2(guint8 command, guint8 param)
+{
+ guint8 channel;
+ guint8 oper;
+
+ channel = command & 0x0f;
+ oper = command & 0xf0;
+ switch (oper) {
+ case 0xc0: { /* change instrument */
+ int inst = param;
+ instr[channel] = inst; /* XXXX offset? */
+ // SEQ_SET_PATCH(dev, channel, inst);
+ // SEQ_DUMPBUF();
+ return 0;
+ }
+ default:
+ printf("ADLIB: Unknown event %02x\n", command);
+ }
+
+ SEQ_DUMPBUF();
+ return 0;
+}
+
+static int
+midi_adlib_event(byte command, int argc, byte *argv)
+{
+ if (argc > 1)
+ return midi_adlib_event1(command, argv[0], argv[1]);
+ else
+ return midi_adlib_event2(command, argv[0]);
+}
+
+static int
+midi_adlib_delay(int ticks)
+{
+ SEQ_DELTA_TIME(ticks);
+ return SFX_OK;
+}
+
+static int
+midi_adlib_set_option(char *name, char *value)
+{
+ return SFX_ERROR; /* No options are supported at this time */
+}
+
+/* the driver struct */
+
+sfx_sequencer_t sfx_sequencer_oss_adlib = {
+ "adlib",
+ "0.1",
+ SFX_DEVICE_NONE, /* No device dependancy-- fixme, this might become ossseq */
+ &midi_adlib_set_option,
+ &midi_adlib_open,
+ &midi_adlib_close,
+ &midi_adlib_event,
+ &midi_adlib_delay,
+ NULL,
+ &midi_adlib_allstop,
+ NULL,
+ &midi_adlib_reverb,
+ 003, /* patch.003 */
+ SFX_SEQ_PATCHFILE_NONE,
+ 0x04, /* playflag */
+ 0, /* do not play channel 9 */
+ ADLIB_VOICES, /* Max polyphony */
+ 0 /* Does not require any write-ahead by its own */
+};
+
+#endif /* HAVE_SYS_SOUNDCARD_H */
diff --git a/engines/sci/sfx/seq/sequencers.c b/engines/sci/sfx/seq/sequencers.c
new file mode 100644
index 0000000000..cb43381fa9
--- /dev/null
+++ b/engines/sci/sfx/seq/sequencers.c
@@ -0,0 +1,68 @@
+/***************************************************************************
+ sequencers.c Copyright (C) 2004 Christoph Reichenbach
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+
+#include "../sequencer.h"
+#include <resource.h>
+
+#ifndef SCUMMVM
+extern sfx_sequencer_t sfx_sequencer_gm;
+extern sfx_sequencer_t sfx_sequencer_mt32;
+#ifdef HAVE_SYS_SOUNDCARD_H
+extern sfx_sequencer_t sfx_sequencer_oss_adlib;
+#endif
+#endif // SCUMMVM
+
+sfx_sequencer_t *sfx_sequencers[] = {
+#ifndef SCUMMVM
+ &sfx_sequencer_gm,
+ &sfx_sequencer_mt32,
+#ifdef HAVE_SYS_SOUNDCARD_H
+ &sfx_sequencer_oss_adlib,
+#endif
+#endif // SCUMMVM
+ NULL
+};
+
+
+sfx_sequencer_t *
+sfx_find_sequencer(char *name)
+{
+ if (!name) {
+ /* Implement default policy for your platform (if any) here, or in a function
+ ** called from here (if it's non-trivial). Try to use midi_devices[0], if
+ ** feasible. */
+
+ return sfx_sequencers[0]; /* default */
+ } else {
+ int n = 0;
+ while (sfx_sequencers[n]
+ && strcasecmp(sfx_sequencers[n]->name, name))
+ ++n;
+
+ return sfx_sequencers[n];
+ }
+}
diff --git a/engines/sci/sfx/sequencer.h b/engines/sci/sfx/sequencer.h
new file mode 100644
index 0000000000..586e4b3e51
--- /dev/null
+++ b/engines/sci/sfx/sequencer.h
@@ -0,0 +1,142 @@
+/***************************************************************************
+ sfx_sequencer.h, from
+ midi_device.h Copyright (C) 2001 Solomon Peachy
+ Copytight (C) 2002 Christoph Reichenbach
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+
+#ifndef _SFX_SEQUENCER_H_
+#define _SFX_SEQUENCER_H_
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <sfx_core.h>
+#include <stdio.h>
+#include "device.h"
+#include <scitypes.h>
+
+#define SFX_SEQ_PATCHFILE_NONE -1
+
+typedef struct _sfx_sequencer {
+ const char *name; /* Sequencer name */
+ const char *version; /* Sequencer version */
+
+ int device; /* Type of device the sequencer depends on, may be SFX_DEVICE_NONE. */
+
+ int
+ (*set_option)(char *name, char *value);
+ /* Sets an option for the sequencing mechanism
+ ** Parameters: (char *) name: The name describing what to set
+ ** (char *) value: The value to set
+ ** Returns : (int) SFX_OK, or SFX_ERROR if the name wasn't understood
+ */
+
+ int
+ (*open)(int patch_len, byte *patch, int patch2_len, byte *patch2, void *device);
+ /* Opens the sequencer for writing
+ ** Parameters: (int) patch_len, patch2_len: Length of the patch data
+ ** (byte *) patch, patch2: Bulk patch data
+ ** (void *) device: A device matching the 'device' property, or NULL
+ ** if the property is null.
+ ** Returns : (int) SFX_OK on success, SFX_ERROR otherwise
+ ** The device should be initialized to a tick frequency of 60 Hz.
+ ** 'patch' and 'patch_len' refer to the patch resource passed to open,
+ ** as specified by the 'patchfile' property. 'patch' may be NULL if the
+ ** resource wasn't found.
+ ** For more information regarding patch resources, please refer to the
+ ** FreeSCI documentation, particularly the part regarding 'patch.*' resource
+ ** data.
+ */
+
+ int (*close)(void);
+ /* Closes the sequencer
+ ** Returns : SFX_OK on success, SFX_ERROR otherwise
+ */
+
+ int (*event)(byte command, int argc, byte *argv);
+ /* Plays a MIDI event
+ ** Parameters: (byte) command: MIDI command to play
+ ** (int) argc: Number of arguments to the command
+ ** (byte *) argv: Pointer to additional arguments
+ ** Returns : SFX_OK on success, SFX_ERROR otherwise
+ ** argv is guaranteed to point to a sufficiently large number of
+ ** arguments, as indicated by 'command' and the MIDI standard.
+ ** No 'running status' will be passed, 'command' will always be
+ ** explicit.
+ */
+ int (*delay)(int ticks); /* OPTIONAL -- may be NULL, but highly recommended */
+ /* Inserts a delay (delta time) into the sequencer cue
+ ** Parameters: (int) ticks: Number of 60 Hz ticks to delay
+ ** Returns : SFX_OK on success, SFX_ERROR otherwise
+ */
+
+ int (*reset_timer)(GTimeVal ts);
+ /* OPTIONAL -- may be NULL, but highly recommended in combination with delay() */
+ /* Resets the timer counter associated with the 'delay()' function
+ ** Parameters: (GTimeVal) ts: Timestamp of the base time
+ ** Returns : SFX_OK on success, SFX_ERROR otherwise
+ */
+
+ int (*allstop)(void); /* OPTIONAL -- may be NULL */
+ /* Stops playing everything in the sequencer queue
+ ** Returns : SFX_OK on success, SFX_ERROR otherwise
+ */
+
+ int (*volume)(guint8 volume); /* OPTIONAL -- can be NULL */
+ /* Sets the sequencer volume
+ ** Parameters; (byte) volume: The volume to set, with 0 being mute and 127 full volume
+ ** Returns : SFX_OK on success, SFX_ERROR otherwise
+ */
+
+ int (*reverb)(int param); /* OPTIONAL -- may be NULL */
+ /* Sets the device reverb
+ ** Parameters; (int) param: The reverb to set
+ ** Returns : SFX_OK on success, SFX_ERROR otherwise
+ */
+
+ int patchfile, patchfile2; /* Patch resources to pass into the call to open(),
+ ** if present, or SFX_SEQ_PATCHFILE_NONE */
+ guint8 playmask; /* SCI 'playflag' mask to determine which SCI song channels
+ ** this sequencer should play */
+ /* 0x01 -- MT-32
+ ** 0x02 -- Yamaha FB-01
+ ** 0x04 -- CMS or Game Blaster
+ ** 0x08 -- Casio MT540 or CT460
+ ** 0x10 -- Tandy 3-voice
+ ** 0x20 -- PC speaker
+ */
+ guint8 play_rhythm; /* Plays the rhythm channel? */
+ gint8 polyphony; /* Device polyphony (# of voices) */
+
+ int min_write_ahead_ms; /* Minimal write-ahead, in milliseconds */
+ /* Note that write-ahead is tuned automatically; this enforces a lower limit */
+
+} sfx_sequencer_t;
+
+
+sfx_sequencer_t *
+sfx_find_sequencer(char *name);
+/* Finds a sequencer by name
+** Parameters: (char *) name: Name of the sequencer to look up, or NULL for default
+** Returns : (sfx_sequencer_t *) The sequencer of matching name, or NULL
+** if not found
+*/
+
+
+#endif /* _SFX_SEQUENCER_H_ */
diff --git a/engines/sci/sfx/softseq.h b/engines/sci/sfx/softseq.h
new file mode 100644
index 0000000000..38efac6d59
--- /dev/null
+++ b/engines/sci/sfx/softseq.h
@@ -0,0 +1,134 @@
+/***************************************************************************
+ sfx_softseq.h Copyright (C) 2004 Christoph Reichenbach
+
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public Licence as
+ published by the Free Software Foundaton; either version 2 of the
+ Licence, or (at your option) any later version.
+
+ It is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ merchantibility or fitness for a particular purpose. See the
+ GNU General Public Licence for more details.
+
+ You should have received a copy of the GNU General Public Licence
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+
+ Please contact the maintainer for any program-related bug reports or
+ inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+
+#ifndef SFX_SOFTSEQ_H_
+#define SFX_SOFTSEQ_H_
+
+#include <sfx_core.h>
+#include <sfx_pcm.h>
+#include "sequencer.h"
+#include <resource.h>
+
+
+/* Software sequencer */
+typedef struct sfx_softseq {
+ const char *name;
+ const char *version;
+
+ int
+ (*set_option)(struct sfx_softseq *self, char *name, char *value);
+ /* Sets an option for the sequencer
+ ** Parameters: (sfx_softseq_t *) self: Self reference
+ ** (char *) name: Name of the option to set
+ ** (char *0 value: Value to set the option to
+ ** Returns : (int) GFX_OK on success, or GFX_ERROR if not supported
+ */
+
+ int
+ (*init)(struct sfx_softseq *self, byte *res_data, int res_size,
+ byte *res2_data, int res2_size);
+ /* Initialises the sequencer
+ ** Parameters: (sfx_softseq_t *) self: Self reference
+ ** (byte *) res_data: Resource data for 'patch_nr' (see below)
+ ** (int) res_size: Number of bytes in 'res_data'
+ ** (byte *) res2_data: Resource data for 'patch2_nr' (see below)
+ ** (int) res2_size: Number of bytes in 'res2_data'
+ ** Returns : (int) SFX_OK on success, SFX_ERROR otherwise
+ ** Note that 'res_data' is only a valid pointer for this call. If the
+ ** data is needed later during execution, it should be backed up internally.
+ ** If the requested resource is not available, res_data will be NULL
+ ** /even if/ patch_nr is set.
+ */
+
+ void
+ (*exit)(struct sfx_softseq *self);
+ /* Uninitialises the sequencer and frees all used resources
+ ** Parameters: (sfx_softseq_t *) self: Self reference
+ */
+
+ void
+ (*set_volume)(struct sfx_softseq *self, int new_volume);
+ /* Sets the sequencer volume
+ ** Parameters: (sfx_softseq_t *) self: Self reference
+ ** (int) new_volume: A volume, between 0 (quiet) and 127 (max)
+ */
+
+ void
+ (*handle_command)(struct sfx_softseq *self, byte cmd, int argc, byte *argv);
+ /* Handle a MIDI command
+ ** Parameters: (sfx_softseq_t *) self: Self reference
+ ** (byte) cmd: Basic MIDI command, always includes command and channel
+ ** (int) argc: Number of additional arguments to this command
+ ** (byte *) argv: Additional arguments to 'cmd'
+ */
+
+ void
+ (*poll)(struct sfx_softseq *self, byte *dest, int len);
+ /* Asks the software sequencer to fill in parts of a buffer
+ ** Parameters: (sfx_softseq_t *) self: Self reference
+ ** (int) len: Number of _frames_ to write
+ ** Returns : (byte) *dest: 'len' frames must be written to this buffer
+ */
+
+ void
+ (*allstop)(struct sfx_softseq *self);
+ /* Stops all sound generation
+ ** Parameters: (sfx_softseq_t *) self: Self reference
+ */
+
+ void *internal; /* Internal data, may be used by sfx_softseq_t inmplementors */
+
+ int patch_nr; /* Number of the first patch file associated with this sequencer,
+ ** or SFX_SEQ_PATCHFILE_NONE */
+ int patch2_nr; /* Number of the second patch file associated with this sequencer,
+ ** or SFX_SEQ_PATCHFILE_NONE */
+ int playmask; /* playflag identifying the device emulated */
+ /* 0x01 -- MT-32
+ ** 0x02 -- Yamaha FB-01
+ ** 0x04 -- CMS or Game Blaster
+ ** 0x08 -- Casio MT540 or CT460
+ ** 0x10 -- Tandy 3-voice
+ ** 0x20 -- PC speaker
+ */
+ int play_rhythm; /* Whether the rhythm channel (9) should be played */
+ int polyphony; /* Number of voices played */
+
+ sfx_pcm_config_t pcm_conf; /* Setup of the channel the sequencer writes to */
+
+} sfx_softseq_t;
+
+
+sfx_softseq_t *
+sfx_find_softseq(char *name);
+/* Finds a given or default software sequencer
+** Parameters: (char *) name: Name of the sequencer to look up, or NULL for default
+** Returns : (sfx_softseq_t *) The requested sequencer, or NULL if not found
+*/
+
+#endif /* !defined(SFX_SOFTSEQ_H_) */
diff --git a/engines/sci/sfx/softseq/Makefile.am b/engines/sci/sfx/softseq/Makefile.am
new file mode 100644
index 0000000000..af7f8ca2c9
--- /dev/null
+++ b/engines/sci/sfx/softseq/Makefile.am
@@ -0,0 +1,5 @@
+noinst_LIBRARIES = libscisoftseq.a
+INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@
+libscisoftseq_a_SOURCES = softsequencers.c pcspeaker.c SN76496.c fmopl.c opl2.c amiga.c \
+ fluidsynth.c
+EXTRA_DIST = fmopl.h
diff --git a/engines/sci/sfx/softseq/SN76496.c b/engines/sci/sfx/softseq/SN76496.c
new file mode 100644
index 0000000000..cb32b89f0c
--- /dev/null
+++ b/engines/sci/sfx/softseq/SN76496.c
@@ -0,0 +1,248 @@
+/***************************************************************************
+ SN76496.c Copyright (C) 2004 Christoph Reichenbach
+
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public Licence as
+ published by the Free Software Foundaton; either version 2 of the
+ Licence, or (at your option) any later version.
+
+ It is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ merchantibility or fitness for a particular purpose. See the
+ GNU General Public Licence for more details.
+
+ You should have received a copy of the GNU General Public Licence
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+
+ Please contact the maintainer for any program-related bug reports or
+ inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+/* Tandy/PCJr sequencer for FreeSCI */
+
+#include "../softseq.h"
+#include <sci_midi.h>
+
+#define FREQUENCY 44100
+#define CHANNELS_NR 3
+#define VOLUME_SHIFT 3
+
+static int global_volume = 100; /* Base volume */
+static int volumes[CHANNELS_NR] = { 100, 100, 100 };
+static int notes[CHANNELS_NR] = {0, 0, 0}; /* Current halftone, or 0 if off */
+static int freq_count[CHANNELS_NR] = {0, 0, 0};
+static int channel_assigner = 0;
+static int channels_assigned = 0;
+static int chan_nrs[CHANNELS_NR] = {-1, -1, -1};
+
+extern sfx_softseq_t sfx_softseq_pcspeaker;
+/* Forward-declare the sequencer we are defining here */
+
+
+static int
+SN76496_set_option(sfx_softseq_t *self, char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+static int
+SN76496_init(sfx_softseq_t *self, byte *patch, int patch_len, byte *patch2,
+ int patch2_len)
+{
+ return SFX_OK;
+}
+
+static void
+SN76496_exit(sfx_softseq_t *self)
+{
+}
+
+static void
+SN76496_event(sfx_softseq_t *self, byte command, int argc, byte *argv)
+{
+ int i;
+ int chan = -1;
+#if 0
+ fprintf(stderr, "Note [%02x : %02x %02x]\n", command, argc?argv[0] : 0, (argc > 1)? argv[1] : 0);
+#endif
+ if ((command & 0xe0) == 0x80) {
+ int chan_nr = command & 0xf;
+
+ /* First, test for channel having been assigned already */
+ if (channels_assigned & (1 << chan_nr)) {
+ /* Already assigned this channel number: */
+ for (i = 0; i < CHANNELS_NR; i++)
+ if (chan_nrs[i] == chan_nr) {
+ chan = i;
+ break;
+ }
+ } else {
+ /* Assign new channel round-robin */
+
+ /* Mark channel as unused: */
+ if (chan_nrs[channel_assigner] >= 0)
+ channels_assigned &= ~(1 << chan_nrs[channel_assigner]);
+
+ /* Remember channel: */
+ chan_nrs[channel_assigner] = chan_nr;
+ /* Mark channel as used */
+ channels_assigned |= (1 << chan_nrs[channel_assigner]);
+
+ /* Save channel for use later in this call: */
+ chan = channel_assigner;
+ /* Round-ropin iterate channel assigner: */
+ channel_assigner = (channel_assigner + 1) % CHANNELS_NR;
+ }
+ }
+#if 0
+ fprintf(stderr, " --> %d [%04x], {%d,%d,%d}@%d\n", chan,
+ channels_assigned, chan_nrs[0],chan_nrs[1],chan_nrs[2],channel_assigner);
+#endif
+
+ switch (command & 0xf0) {
+
+ case 0x80:
+ if (argv[0] == notes[chan])
+ notes[chan] = 0;
+ break;
+
+ case 0x90:
+ if (!argv[1]) {
+ if (argv[chan] == notes[chan])
+ notes[chan] = 0;
+ } else {
+ notes[chan] = argv[0];
+ volumes[chan] = argv[1];
+ }
+ break;
+
+ case 0xb0:
+ if (argv[1] == SCI_MIDI_CHANNEL_NOTES_OFF)
+ notes[chan] = 0;
+ break;
+
+
+ default:
+#if DEBUG
+ fprintf(stderr, "[SFX:PCM-PC] Unused MIDI command %02x %02x %02x\n", command, argc?argv[0] : 0, (argc > 1)? argv[1] : 0);
+#endif
+ break; /* ignore */
+ }
+}
+
+#define BASE_NOTE 129 /* A10 */
+#define BASE_OCTAVE 10 /* A10, as I said */
+
+static int
+freq_table[12] = { /* A4 is 440Hz, halftone map is x |-> ** 2^(x/12) */
+ 28160, /* A10 */
+ 29834,
+ 31608,
+ 33488,
+ 35479,
+ 37589,
+ 39824,
+ 42192,
+ 44701,
+ 47359,
+ 50175,
+ 53159
+};
+
+static inline int
+get_freq(int note)
+{
+ int halftone_delta = note - BASE_NOTE;
+ int oct_diff = ((halftone_delta + BASE_OCTAVE * 12) / 12) - BASE_OCTAVE;
+ int halftone_index = (halftone_delta + (12*100)) % 12 ;
+ int freq = (!note)? 0 : freq_table[halftone_index] / (1 << (-oct_diff));
+
+ return freq;
+}
+
+
+void
+SN76496_poll(sfx_softseq_t *self, byte *dest, int len)
+{
+ gint16 *buf = (gint16 *) dest;
+ int i;
+ int chan;
+ int freq[CHANNELS_NR];
+
+ for (chan = 0; chan < CHANNELS_NR; chan++)
+ freq[chan] = get_freq(notes[chan]);
+
+ for (i = 0; i < len; i++) {
+ int result = 0;
+
+ for (chan = 0; chan < CHANNELS_NR; chan++)
+ if (notes[chan]) {
+ int volume = (global_volume * volumes[chan])
+ >> VOLUME_SHIFT;
+
+ freq_count[chan] += freq[chan];
+ while (freq_count[chan] >= (FREQUENCY << 1))
+ freq_count[chan] -= (FREQUENCY << 1);
+
+ if (freq_count[chan] - freq[chan] < 0) {
+ /* Unclean rising edge */
+ int l = volume << 1;
+ result += -volume + (l*freq_count[chan])/freq[chan];
+ } else if (freq_count[chan] >= FREQUENCY
+ && freq_count[chan] - freq[chan] < FREQUENCY) {
+ /* Unclean falling edge */
+ int l = volume << 1;
+ result += volume - (l*(freq_count[chan] - FREQUENCY))/freq[chan];
+ } else {
+ if (freq_count[chan] < FREQUENCY)
+ result += volume;
+ else
+ result += -volume;
+ }
+ }
+ buf[i] = result;
+ }
+
+}
+
+void
+SN76496_allstop(sfx_softseq_t *self)
+{
+ int i;
+ for (i = 0; i < CHANNELS_NR; i++)
+ notes[i] = 0;
+}
+
+void
+SN76496_volume(sfx_softseq_t *self, int new_volume)
+{
+ global_volume = new_volume;
+}
+
+
+sfx_softseq_t sfx_softseq_SN76496 = {
+ "SN76496",
+ "0.1",
+ SN76496_set_option,
+ SN76496_init,
+ SN76496_exit,
+ SN76496_volume,
+ SN76496_event,
+ SN76496_poll,
+ SN76496_allstop,
+ NULL,
+ SFX_SEQ_PATCHFILE_NONE,
+ SFX_SEQ_PATCHFILE_NONE,
+ 0x10, /* Tandy/PCJr channels */
+ 0, /* No rhythm channel */
+ 3, /* # of voices */
+ {FREQUENCY, SFX_PCM_MONO, SFX_PCM_FORMAT_S16_NATIVE}
+};
diff --git a/engines/sci/sfx/softseq/amiga.c b/engines/sci/sfx/softseq/amiga.c
new file mode 100644
index 0000000000..12240897d7
--- /dev/null
+++ b/engines/sci/sfx/softseq/amiga.c
@@ -0,0 +1,658 @@
+/***************************************************************************
+ amiga.c Copyright (C) 2007 Walter van Niftrik
+
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public Licence as
+ published by the Free Software Foundaton; either version 2 of the
+ Licence, or (at your option) any later version.
+
+ It is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ merchantibility or fitness for a particular purpose. See the
+ GNU General Public Licence for more details.
+
+ You should have received a copy of the GNU General Public Licence
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+
+ Please contact the maintainer for any program-related bug reports or
+ inquiries.
+
+ Current Maintainer:
+
+ Walter van Niftrik <w.f.b.w.v.niftrik@stud.tue.nl>
+
+***************************************************************************/
+
+#include "resource.h"
+#include "sci_memory.h"
+#include "../softseq.h"
+
+#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 */
+
+typedef struct envelope {
+ /* Phase period length in samples */
+ int length;
+ /* Velocity delta per period */
+ int delta;
+ /* Target velocity */
+ int target;
+} envelope_t;
+
+/* Fast decay envelope */
+static envelope_t env_decay = {FREQUENCY / (32 * 64), 1, 0};
+
+typedef struct instrument {
+ 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];
+ sbyte *samples;
+ sbyte *loop;
+} instrument_t;
+
+typedef struct bank {
+ char name[30];
+ int size;
+ instrument_t *instruments[256];
+} bank_t;
+
+typedef struct channel {
+ 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;
+} channel_t;
+
+typedef struct hw_channel {
+ int instrument;
+ int volume;
+ int pan;
+} hw_channel_t;
+
+/* 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 */
+static 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
+};
+
+static void
+set_envelope(channel_t *channel, envelope_t *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;
+}
+
+static inline int
+interpolate(sbyte *samples, frac_t offset)
+{
+ int x = frac_to_int(offset);
+ int diff = (samples[x + 1] - samples[x]) << 8;
+
+ return (samples[x] << 8) + frac_to_int(diff * (offset & FRAC_LO_MASK));
+}
+
+static void
+play_instrument(gint16 *dest, channel_t *channel, int count)
+{
+ int index = 0;
+ int vol = hw_channels[channel->hw_channel].volume;
+ instrument_t *instrument = bank.instruments[channel->instrument];
+
+ while (1) {
+ /* Available source samples until end of segment */
+ frac_t lin_avail;
+ int seg_end, rem, i, amount;
+ sbyte *samples;
+
+ if (channel->looping) {
+ samples = instrument->loop;
+ seg_end = instrument->loop_size;
+ }
+ else {
+ samples = instrument->samples;
+ seg_end = instrument->size;
+ }
+
+ lin_avail = int_to_frac(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 > 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);
+ channel->offset += channel->rate;
+ }
+
+ if (channel->envelope_samples != -1)
+ channel->envelope_samples -= amount;
+
+ if (channel->envelope_samples == 0) {
+ envelope_t *envelope;
+ int delta, target, velocity;
+
+ if (channel->decay)
+ envelope = &env_decay;
+ 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 */
+ set_envelope(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 (frac_to_int(channel->offset) >= seg_end) {
+ if (instrument->mode & MODE_LOOP) {
+ /* Loop the samples */
+ channel->offset -= int_to_frac(seg_end);
+ channel->looping = 1;
+ } else {
+ /* All samples have been played */
+ channel->note = -1;
+ break;
+ }
+ }
+ }
+}
+
+static void
+change_instrument(int channel, int instrument)
+{
+#ifdef DEBUG
+ if (bank.instruments[instrument])
+ sciprintf("[sfx:seq:amiga] Setting channel %i to \"%s\" (%i)\n", channel, bank.instruments[instrument]->name, instrument);
+ else
+ sciprintf("[sfx:seq:amiga] Warning: instrument %i does not exist (channel %i)\n", instrument, channel);
+#endif
+ hw_channels[channel].instrument = instrument;
+}
+
+static void
+stop_channel(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) {
+ /* Trigger fast decay envelope */
+ channels[i].decay = 1;
+ channels[i].envelope_samples = env_decay.length;
+ break;
+ }
+}
+
+static void
+stop_note(int ch, int note)
+{
+ int channel;
+ instrument_t *instrument;
+
+ for (channel = 0; channel < CHANNELS_NR; channel++)
+ if (channels[channel].note == note && channels[channel].hw_channel == ch && !channels[channel].decay)
+ break;
+
+ if (channel == CHANNELS_NR) {
+#ifdef DEBUG
+ sciprintf("[sfx:seq:amiga] Warning: cannot stop note %i on channel %i\n", note, ch);
+#endif
+ return;
+ }
+
+ instrument = bank.instruments[channels[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);
+}
+
+static void
+start_note(int ch, int note, int velocity)
+{
+ instrument_t *instrument;
+ int channel;
+
+ if (hw_channels[ch].instrument < 0 || hw_channels[ch].instrument > 255) {
+ sciprintf("[sfx:seq:amiga] Error: invalid instrument %i on channel %i\n", hw_channels[ch].instrument, ch);
+ return;
+ }
+
+ instrument = bank.instruments[hw_channels[ch].instrument];
+
+ if (!instrument) {
+ sciprintf("[sfx:seq:amiga] Error: instrument %i does not exist\n", hw_channels[ch].instrument);
+ return;
+ }
+
+ for (channel = 0; channel < CHANNELS_NR; channel++)
+ if (channels[channel].note == -1)
+ break;
+
+ if (channel == CHANNELS_NR) {
+ sciprintf("[sfx:seq:amiga] Warning: could not find a free channel\n");
+ return;
+ }
+
+ stop_channel(ch);
+
+ if (instrument->mode & MODE_PITCH) {
+ int fnote = note + instrument->transpose;
+
+ if (fnote < 0 || fnote > 127) {
+ sciprintf("[sfx:seq:amiga] Error: illegal note %i\n", fnote);
+ return;
+ }
+
+ /* Compute rate for note */
+ channels[channel].rate = double_to_frac(freq_table[fnote] / (double) FREQUENCY);
+ }
+ else
+ channels[channel].rate = double_to_frac(BASE_FREQ / (double) FREQUENCY);
+
+ channels[channel].instrument = hw_channels[ch].instrument;
+ channels[channel].note = note;
+ channels[channel].note_velocity = velocity;
+
+ if ((instrument->mode & MODE_LOOP) && (instrument->envelope[0].length != 0))
+ set_envelope(&channels[channel], instrument->envelope, 0);
+ else {
+ channels[channel].velocity = 64;
+ channels[channel].envelope_samples = -1;
+ }
+
+ channels[channel].offset = 0;
+ channels[channel].hw_channel = ch;
+ channels[channel].decay = 0;
+ channels[channel].looping = 0;
+}
+
+static gint16 read_int16(byte *data)
+{
+ return (data[0] << 8) | data[1];
+}
+
+static gint32 read_int32(byte *data)
+{
+ return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
+}
+
+static instrument_t *read_instrument(FILE *file, int *id)
+{
+ instrument_t *instrument;
+ byte header[61];
+ int size;
+ int seg_size[3];
+ int loop_offset;
+ int i;
+
+ if (fread(header, 1, 61, file) < 61) {
+ sciprintf("[sfx:seq:amiga] Error: failed to read instrument header\n");
+ return NULL;
+ }
+
+ instrument = (instrument_t *) sci_malloc(sizeof(instrument_t));
+
+ seg_size[0] = read_int16(header + 35) * 2;
+ seg_size[1] = read_int16(header + 41) * 2;
+ seg_size[2] = read_int16(header + 47) * 2;
+
+ instrument->mode = header[33];
+ instrument->transpose = (gint8) header[34];
+ for (i = 0; i < 4; i++) {
+ int length = (gint8) header[49 + i];
+
+ if (length == 0 && i > 0)
+ length = 256;
+
+ instrument->envelope[i].length = length * FREQUENCY / 60;
+ instrument->envelope[i].delta = (gint8) 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;
+ size = seg_size[0] + seg_size[1] + seg_size[2];
+
+ *id = read_int16(header);
+
+ strncpy(instrument->name, (char *) header + 2, 29);
+ instrument->name[29] = 0;
+#ifdef DEBUG
+ sciprintf("[sfx:seq:amiga] Reading instrument %i: \"%s\" (%i bytes)\n",
+ *id, instrument->name, size);
+ sciprintf(" Mode: %02x\n", instrument->mode);
+ sciprintf(" Looping: %s\n", instrument->mode & MODE_LOOP ? "on" : "off");
+ sciprintf(" Pitch changes: %s\n", instrument->mode & MODE_PITCH ? "on" : "off");
+ sciprintf(" Segment sizes: %i %i %i\n", seg_size[0], seg_size[1], seg_size[2]);
+ sciprintf(" Segment offsets: 0 %i %i\n", loop_offset, read_int32(header + 43));
+#endif
+ instrument->samples = (sbyte *) sci_malloc(size + 1);
+ if (fread(instrument->samples, 1, size, file) < size) {
+ sciprintf("[sfx:seq:amiga] Error: failed to read instrument samples\n");
+ return NULL;
+ }
+
+ if (instrument->mode & MODE_LOOP) {
+ if (loop_offset + seg_size[1] > size) {
+#ifdef DEBUG
+ sciprintf("[sfx:seq:amiga] Warning: looping samples extend %i bytes past end of sample block\n",
+ loop_offset + seg_size[1] - size);
+#endif
+ seg_size[1] = size - loop_offset;
+ }
+
+ if (seg_size[1] < 0) {
+ sciprintf("[sfx:seq:amiga] Error: invalid looping point\n");
+ return NULL;
+ }
+
+ instrument->size = seg_size[0];
+ instrument->loop_size = seg_size[1];
+
+ instrument->loop = (sbyte*)sci_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->size = size;
+ instrument->samples[instrument->size] = 0;
+ }
+
+ return instrument;
+}
+
+static int
+ami_set_option(sfx_softseq_t *self, char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+static int
+ami_init(sfx_softseq_t *self, byte *patch, int patch_len, byte *patch2, int patch2_len)
+{
+ FILE *file;
+ byte header[40];
+ int i;
+
+ file = sci_fopen("bank.001", "rb");
+
+ if (!file) {
+ sciprintf("[sfx:seq:amiga] Error: file bank.001 not found\n");
+ return SFX_ERROR;
+ }
+
+ if (fread(header, 1, 40, file) < 40) {
+ sciprintf("[sfx:seq:amiga] Error: failed to read header of file bank.001\n");
+ fclose(file);
+ return SFX_ERROR;
+ }
+
+ for (i = 0; i < 256; i++)
+ bank.instruments[i] = NULL;
+
+ for (i = 0; i < CHANNELS_NR; i++) {
+ channels[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);
+ }
+
+ bank.size = read_int16(header + 38);
+ strncpy(bank.name, (char *) header + 8, 29);
+ bank.name[29] = 0;
+#ifdef DEBUG
+ sciprintf("[sfx:seq:amiga] Reading %i instruments from bank \"%s\"\n", bank.size, bank.name);
+#endif
+
+ for (i = 0; i < bank.size; i++) {
+ int id;
+ instrument_t *instrument = read_instrument(file, &id);
+
+ if (!instrument) {
+ sciprintf("[sfx:seq:amiga] Error: failed to read bank.001\n");
+ fclose(file);
+ return SFX_ERROR;
+ }
+
+ if (id < 0 || id > 255) {
+ sciprintf("[sfx:seq:amiga] Error: instrument ID out of bounds\n");
+ return SFX_ERROR;
+ }
+
+ bank.instruments[id] = instrument;
+ }
+
+ fclose(file);
+
+ return SFX_OK;
+}
+
+static void
+ami_exit(sfx_softseq_t *self)
+{
+ int i;
+
+ for (i = 0; i < bank.size; i++) {
+ if (bank.instruments[i]) {
+ sci_free(bank.instruments[i]->samples);
+ sci_free(bank.instruments[i]);
+ }
+ }
+}
+
+static void
+ami_event(sfx_softseq_t *self, byte command, int argc, byte *argv)
+{
+ int channel, oper;
+
+ channel = command & 0x0f;
+ oper = command & 0xf0;
+
+ if (channel >= HW_CHANNELS_NR) {
+#ifdef DEBUG
+ sciprintf("[sfx:seq:amiga] Warning: received event for non-existing channel %i\n", channel);
+#endif
+ return;
+ }
+
+ switch(oper) {
+ case 0x90:
+ if (argv[1] > 0)
+ start_note(channel, argv[0], argv[1]);
+ else
+ stop_note(channel, argv[0]);
+ break;
+ case 0xb0:
+ switch (argv[0]) {
+ case 0x07:
+ hw_channels[channel].volume = argv[1];
+ break;
+ case 0x0a:
+#ifdef DEBUG
+ sciprintf("[sfx:seq:amiga] Warning: ignoring pan 0x%02x event for channel %i\n", argv[1], channel);
+#endif
+ break;
+ case 0x7b:
+ stop_channel(channel);
+ break;
+ default:
+ sciprintf("[sfx:seq:amiga] Warning: unknown control event 0x%02x\n", argv[0]);
+ }
+ break;
+ case 0xc0:
+ change_instrument(channel, argv[0]);
+ break;
+ default:
+ sciprintf("[sfx:seq:amiga] Warning: unknown event %02x\n", command);
+ }
+}
+
+void
+ami_poll(sfx_softseq_t *self, byte *dest, int len)
+{
+ int i, j;
+ gint16 *buf = (gint16 *) dest;
+ gint16 *buffers = (gint16*)malloc(len * 2 * CHANNELS_NR);
+
+ memset(buffers, 0, len * 2 * CHANNELS_NR);
+ memset(dest, 0, len * 4);
+
+ /* 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);
+
+ for (j = 0; j < len; j++) {
+ int mixedl = 0, mixedr = 0;
+
+ /* 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 */
+ buf[2 * j] = mixedl * volume >> 16;
+ buf[2 * j + 1] = mixedr *volume >> 16;
+ }
+}
+
+void
+ami_volume(sfx_softseq_t *self, int new_volume)
+{
+ volume = new_volume;
+}
+
+void
+ami_allstop(sfx_softseq_t *self)
+{
+ int i;
+ for (i = 0; i < HW_CHANNELS_NR; i++)
+ stop_channel(i);
+}
+
+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}
+};
diff --git a/engines/sci/sfx/softseq/fluidsynth.c b/engines/sci/sfx/softseq/fluidsynth.c
new file mode 100644
index 0000000000..a44d75e5bd
--- /dev/null
+++ b/engines/sci/sfx/softseq/fluidsynth.c
@@ -0,0 +1,262 @@
+/***************************************************************************
+ fluidsynth.c Copyright (C) 2008 Walter van Niftrik
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Walter van Niftrik <w.f.b.w.v.niftrik@stud.tue.nl>
+
+***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_FLUIDSYNTH_H
+
+#include <fluidsynth.h>
+
+#include "../softseq.h"
+#include "../sequencer.h"
+#include "../device.h"
+#include "resource.h"
+
+static sfx_sequencer_t *gmseq;
+static fluid_settings_t* settings;
+static fluid_synth_t* synth;
+static guint8 status;
+static char *soundfont = "/etc/midi/8MBGMSFX.SF2";
+static int rpn[16];
+
+#define SAMPLE_RATE 44100
+#define CHANNELS SFX_PCM_STEREO_LR
+
+/* MIDI writer */
+
+static int
+fluidsynth_midi_init(struct _midi_writer *self)
+{
+ return SFX_OK;
+}
+
+static int
+fluidsynth_midi_set_option(struct _midi_writer *self, char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+static int
+fluidsynth_midi_write(struct _midi_writer *self, unsigned char *buf, int len)
+{
+ if (buf[0] == 0xf0)
+ sciprintf("FluidSynth: Skipping sysex message.\n");
+ else if (len == 2) {
+ guint8 command, channel;
+
+ command = buf[0] & 0xf0;
+ channel = buf[0] & 0x0f;
+
+ switch(command) {
+ case 0xc0:
+ fluid_synth_program_change(synth, channel, buf[1]);
+ break;
+ default:
+ printf("FluidSynth: MIDI command [%02x %02x] not supported\n", buf[0], buf[1]);
+ }
+ }
+ else if (len == 3) {
+ guint8 command, channel;
+
+ command = buf[0] & 0xf0;
+ channel = buf[0] & 0x0f;
+
+ switch(command) {
+ case 0x80:
+ fluid_synth_noteoff(synth, channel, buf[1]);
+ break;
+ case 0x90:
+ fluid_synth_noteon(synth, channel, buf[1], buf[2]);
+ break;
+ case 0xb0:
+ switch (buf[1]) {
+ case 0x06:
+ /* Data Entry Slider - course */
+ if (rpn[channel] == 0)
+ fluid_synth_pitch_wheel_sens(synth, channel, buf[2]);
+ else
+ sciprintf("FluidSynth: RPN %i not supported\n", rpn[channel]);
+ case 0x64:
+ /* Registered Parameter Number (RPN) - fine */
+ rpn[channel] &= ~0x7f;
+ rpn[channel] |= buf[2] & 0x7f;
+ break;
+ case 0x65:
+ /* Registered Parameter Number (RPN) - course */
+ rpn[channel] &= ~0x3f80;
+ rpn[channel] |= (buf[2] & 0x7f) << 7;
+ break;
+ default:
+ fluid_synth_cc(synth, channel, buf[1], buf[2]);
+ }
+ break;
+ case 0xe0:
+ fluid_synth_pitch_bend(synth, channel, (buf[2] << 7) | buf[1]);
+ break;
+ default:
+ sciprintf("FluidSynth: MIDI command [%02x %02x %02x] not supported\n", buf[0], buf[1], buf[2]);
+ }
+ }
+ else
+ sciprintf("FluidSynth: Skipping invalid message of %i bytes.\n", len);
+
+ return SFX_OK;
+}
+
+static void
+fluidsynth_midi_delay(struct _midi_writer *self, int ticks)
+{
+}
+
+static void
+fluidsynth_midi_reset_timer(struct _midi_writer *self)
+{
+}
+
+static void
+fluidsynth_midi_close(struct _midi_writer *self)
+{
+}
+
+static midi_writer_t midi_writer_fluidsynth = {
+ "fluidsynth",
+ fluidsynth_midi_init,
+ fluidsynth_midi_set_option,
+ fluidsynth_midi_write,
+ fluidsynth_midi_delay,
+ NULL,
+ fluidsynth_midi_reset_timer,
+ fluidsynth_midi_close
+};
+
+/* Software sequencer */
+
+static void
+fluidsynth_poll(sfx_softseq_t *self, byte *dest, int count)
+{
+ fluid_synth_write_s16(synth, count, dest, 0, 2, dest + 2, 0, 2);
+}
+
+static int
+fluidsynth_init(sfx_softseq_t *self, byte *data_ptr, int data_length,
+ byte *data2_ptr, int data2_length)
+{
+ int sfont_id;
+ double min, max;
+
+ if (0) {
+ sciprintf("FluidSynth ERROR: Mono sound output not supported.\n");
+ return SFX_ERROR;
+ }
+
+ gmseq = sfx_find_sequencer("General MIDI");
+ if (!gmseq) {
+ sciprintf("FluidSynth ERROR: Unable to find General MIDI sequencer.\n");
+ return SFX_ERROR;
+ }
+
+ settings = new_fluid_settings();
+
+ fluid_settings_getnum_range(settings, "synth.sample-rate", &min, &max);
+ if (SAMPLE_RATE < min || SAMPLE_RATE > max) {
+ sciprintf("FluidSynth ERROR: Sample rate '%i' not supported. Valid "
+ "range is (%i-%i).\n", SAMPLE_RATE, (int) min, (int) max);
+ delete_fluid_settings(settings);
+ return SFX_ERROR;
+ }
+
+ fluid_settings_setnum(settings, "synth.sample-rate", SAMPLE_RATE);
+ fluid_settings_setnum(settings, "synth.gain", 0.5f);
+
+ synth = new_fluid_synth(settings);
+
+ if ((sfont_id = fluid_synth_sfload(synth, soundfont, 1)) < 0) {
+ delete_fluid_synth(synth);
+ delete_fluid_settings(settings);
+ return SFX_ERROR;
+ }
+
+ gmseq->open(data_length, data_ptr, data2_length, data2_ptr,
+ &midi_writer_fluidsynth);
+
+ return SFX_OK;
+}
+
+static void
+fluidsynth_exit(sfx_softseq_t *self)
+{
+ delete_fluid_synth(synth);
+ delete_fluid_settings(settings);
+}
+
+static void
+fluidsynth_allstop(sfx_softseq_t *self)
+{
+ if (gmseq->allstop)
+ gmseq->allstop();
+}
+
+static void
+fluidsynth_volume(sfx_softseq_t *self, int volume)
+{
+ if (gmseq->volume)
+ gmseq->volume(volume);
+}
+
+static int
+fluidsynth_set_option(sfx_softseq_t *self, char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+static void
+fluidsynth_event(sfx_softseq_t *self, byte cmd, int argc, byte *argv)
+{
+ gmseq->event(cmd, argc, argv);
+}
+
+sfx_softseq_t sfx_softseq_fluidsynth = {
+ "fluidsynth",
+ "0.1",
+ fluidsynth_set_option,
+ fluidsynth_init,
+ fluidsynth_exit,
+ fluidsynth_volume,
+ fluidsynth_event,
+ fluidsynth_poll,
+ fluidsynth_allstop,
+ NULL,
+ 004, /* patch.004 */
+ 001, /* patch.001 */
+ 0x01, /* playflag */
+ 1, /* do play channel 9 */
+ 32, /* Max polypgony */
+ {SAMPLE_RATE, CHANNELS, SFX_PCM_FORMAT_S16_NATIVE}
+};
+
+#endif /* HAVE_FLUIDSYNTH_H */
diff --git a/engines/sci/sfx/softseq/fmopl.c b/engines/sci/sfx/softseq/fmopl.c
new file mode 100644
index 0000000000..6ea777c667
--- /dev/null
+++ b/engines/sci/sfx/softseq/fmopl.c
@@ -0,0 +1,1144 @@
+/***************************************************************************
+ fmopl.c Copyright (C) 1999-2000 Tatsuyuki Satoh
+ 2001-2004 The ScummVM project
+ 2002 Solomon Peachy
+ 2004 Walter van Niftrik
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+ LGPL licensed version of MAMEs fmopl (V0.37a modified) by
+ Tatsuyuki Satoh. Included from LGPL'ed AdPlug.
+
+***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifndef INLINE
+# ifdef _MSC_VER
+# define INLINE __inline
+# else
+# define INLINE inline
+# endif
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <math.h>
+
+#include "fmopl.h"
+
+#ifndef PI
+#define PI 3.14159265358979323846
+#endif
+
+/* -------------------- preliminary define section --------------------- */
+/* attack/decay rate time rate */
+#define OPL_ARRATE 141280 /* RATE 4 = 2826.24ms @ 3.6MHz */
+#define OPL_DRRATE 1956000 /* RATE 4 = 39280.64ms @ 3.6MHz */
+
+#define FREQ_BITS 24 /* frequency turn */
+
+/* counter bits = 20 , octerve 7 */
+#define FREQ_RATE (1<<(FREQ_BITS-20))
+#define TL_BITS (FREQ_BITS+2)
+
+/* final output shift , limit minimum and maximum */
+#define OPL_OUTSB (TL_BITS+3-16) /* OPL output final shift 16bit */
+#define OPL_MAXOUT (0x7fff<<OPL_OUTSB)
+#define OPL_MINOUT (-0x8000<<OPL_OUTSB)
+
+/* -------------------- quality selection --------------------- */
+
+/* sinwave entries */
+/* used static memory = SIN_ENT * 4 (byte) */
+#define SIN_ENT 2048
+
+/* output level entries (envelope,sinwave) */
+/* envelope counter lower bits */
+int ENV_BITS;
+/* envelope output entries */
+int EG_ENT;
+
+/* used dynamic memory = EG_ENT*4*4(byte)or EG_ENT*6*4(byte) */
+/* used static memory = EG_ENT*4 (byte) */
+int EG_OFF; /* OFF */
+int EG_DED;
+int EG_DST; /* DECAY START */
+int EG_AED;
+#define EG_AST 0 /* ATTACK START */
+
+#define EG_STEP (96.0/EG_ENT) /* OPL is 0.1875 dB step */
+
+/* LFO table entries */
+#define VIB_ENT 512
+#define VIB_SHIFT (32-9)
+#define AMS_ENT 512
+#define AMS_SHIFT (32-9)
+
+#define VIB_RATE 256
+
+/* -------------------- local defines , macros --------------------- */
+
+/* register number to channel number , slot offset */
+#define SLOT1 0
+#define SLOT2 1
+
+/* envelope phase */
+#define ENV_MOD_RR 0x00
+#define ENV_MOD_DR 0x01
+#define ENV_MOD_AR 0x02
+
+/* -------------------- tables --------------------- */
+static const int slot_array[32]=
+{
+ 0, 2, 4, 1, 3, 5,-1,-1,
+ 6, 8,10, 7, 9,11,-1,-1,
+ 12,14,16,13,15,17,-1,-1,
+#ifdef TWELVE_VOICE
+ 18,20,22,19,21,23,-1,-1
+#else
+ -1,-1,-1,-1,-1,-1,-1,-1
+#endif
+};
+
+static guint32 KSL_TABLE[8 * 16];
+
+static const double KSL_TABLE_SEED[8 * 16] = {
+ /* OCT 0 */
+ 0.000, 0.000, 0.000, 0.000,
+ 0.000, 0.000, 0.000, 0.000,
+ 0.000, 0.000, 0.000, 0.000,
+ 0.000, 0.000, 0.000, 0.000,
+ /* OCT 1 */
+ 0.000, 0.000, 0.000, 0.000,
+ 0.000, 0.000, 0.000, 0.000,
+ 0.000, 0.750, 1.125, 1.500,
+ 1.875, 2.250, 2.625, 3.000,
+ /* OCT 2 */
+ 0.000, 0.000, 0.000, 0.000,
+ 0.000, 1.125, 1.875, 2.625,
+ 3.000, 3.750, 4.125, 4.500,
+ 4.875, 5.250, 5.625, 6.000,
+ /* OCT 3 */
+ 0.000, 0.000, 0.000, 1.875,
+ 3.000, 4.125, 4.875, 5.625,
+ 6.000, 6.750, 7.125, 7.500,
+ 7.875, 8.250, 8.625, 9.000,
+ /* OCT 4 */
+ 0.000, 0.000, 3.000, 4.875,
+ 6.000, 7.125, 7.875, 8.625,
+ 9.000, 9.750, 10.125, 10.500,
+ 10.875, 11.250, 11.625, 12.000,
+ /* OCT 5 */
+ 0.000, 3.000, 6.000, 7.875,
+ 9.000, 10.125, 10.875, 11.625,
+ 12.000, 12.750, 13.125, 13.500,
+ 13.875, 14.250, 14.625, 15.000,
+ /* OCT 6 */
+ 0.000, 6.000, 9.000, 10.875,
+ 12.000, 13.125, 13.875, 14.625,
+ 15.000, 15.750, 16.125, 16.500,
+ 16.875, 17.250, 17.625, 18.000,
+ /* OCT 7 */
+ 0.000, 9.000, 12.000, 13.875,
+ 15.000, 16.125, 16.875, 17.625,
+ 18.000, 18.750, 19.125, 19.500,
+ 19.875, 20.250, 20.625, 21.000
+};
+
+/* sustain lebel table (3db per step) */
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
+
+static int SL_TABLE[16];
+
+static const guint32 SL_TABLE_SEED[16] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 31
+};
+
+#define TL_MAX (EG_ENT * 2) /* limit(tl + ksr + envelope) + sinwave */
+/* TotalLevel : 48 24 12 6 3 1.5 0.75 (dB) */
+/* TL_TABLE[ 0 to TL_MAX ] : plus section */
+/* TL_TABLE[ TL_MAX to TL_MAX+TL_MAX-1 ] : minus section */
+static int *TL_TABLE;
+
+/* pointers to TL_TABLE with sinwave output offset */
+static int **SIN_TABLE;
+
+/* LFO table */
+static int *AMS_TABLE;
+static int *VIB_TABLE;
+
+/* envelope output curve table */
+/* attack + decay + OFF */
+//static int ENV_CURVE[2*EG_ENT+1];
+static int ENV_CURVE[2 * 4096 + 1]; // to keep it static ...
+
+/* multiple table */
+#define ML(a) (int)(a * 2)
+static const guint32 MUL_TABLE[16]= {
+/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 */
+ ML(0.50), ML(1.00), ML(2.00), ML(3.00), ML(4.00), ML(5.00), ML(6.00), ML(7.00),
+ ML(8.00), ML(9.00), ML(10.00), ML(10.00),ML(12.00),ML(12.00),ML(15.00),ML(15.00)
+};
+#undef ML
+
+/* dummy attack / decay rate ( when rate == 0 ) */
+static int RATE_0[16]=
+{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+/* -------------------- static state --------------------- */
+
+/* lock level of common table */
+static int num_lock = 0;
+
+/* work table */
+static void *cur_chip = NULL; /* current chip point */
+/* currenct chip state */
+/* static OPLSAMPLE *bufL,*bufR; */
+static OPL_CH *S_CH;
+static OPL_CH *E_CH;
+OPL_SLOT *SLOT7_1, *SLOT7_2, *SLOT8_1, *SLOT8_2;
+
+static int outd[1];
+static int ams;
+static int vib;
+int *ams_table;
+int *vib_table;
+static int amsIncr;
+static int vibIncr;
+static int feedback2; /* connect for SLOT 2 */
+
+/* --------------------- rebuild tables ------------------- */
+
+#define SC_KSL(mydb) ((guint32) (mydb / (EG_STEP / 2)))
+#define SC_SL(db) (int)(db * ((3 / EG_STEP) * (1 << ENV_BITS))) + EG_DST
+
+void OPLBuildTables(int ENV_BITS_PARAM, int EG_ENT_PARAM) {
+ int i;
+
+ ENV_BITS = ENV_BITS_PARAM;
+ EG_ENT = EG_ENT_PARAM;
+ EG_OFF = ((2 * EG_ENT)<<ENV_BITS); /* OFF */
+ EG_DED = EG_OFF;
+ EG_DST = (EG_ENT << ENV_BITS); /* DECAY START */
+ EG_AED = EG_DST;
+ //EG_STEP = (96.0/EG_ENT);
+
+ for (i = 0; i < (int)(sizeof(KSL_TABLE_SEED) / sizeof(double)); i++)
+ KSL_TABLE[i] = SC_KSL(KSL_TABLE_SEED[i]);
+
+ for (i = 0; i < (int)(sizeof(SL_TABLE_SEED) / sizeof(guint32)); i++)
+ SL_TABLE[i] = SC_SL(SL_TABLE_SEED[i]);
+}
+
+#undef SC_KSL
+#undef SC_SL
+
+/* --------------------- subroutines --------------------- */
+
+INLINE int Limit(int val, int max, int min) {
+ if ( val > max )
+ val = max;
+ else if ( val < min )
+ val = min;
+
+ return val;
+}
+
+/* status set and IRQ handling */
+INLINE void OPL_STATUS_SET(FM_OPL *OPL, int flag) {
+ /* set status flag */
+ OPL->status |= flag;
+ if(!(OPL->status & 0x80)) {
+ if(OPL->status & OPL->statusmask) { /* IRQ on */
+ OPL->status |= 0x80;
+ /* callback user interrupt handler (IRQ is OFF to ON) */
+ if(OPL->IRQHandler)
+ (OPL->IRQHandler)(OPL->IRQParam,1);
+ }
+ }
+}
+
+/* status reset and IRQ handling */
+INLINE void OPL_STATUS_RESET(FM_OPL *OPL, int flag) {
+ /* reset status flag */
+ OPL->status &= ~flag;
+ if((OPL->status & 0x80)) {
+ if (!(OPL->status & OPL->statusmask)) {
+ OPL->status &= 0x7f;
+ /* callback user interrupt handler (IRQ is ON to OFF) */
+ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0);
+ }
+ }
+}
+
+/* IRQ mask set */
+INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL, int flag) {
+ OPL->statusmask = flag;
+ /* IRQ handling check */
+ OPL_STATUS_SET(OPL,0);
+ OPL_STATUS_RESET(OPL,0);
+}
+
+/* ----- key on ----- */
+INLINE void OPL_KEYON(OPL_SLOT *SLOT) {
+ /* sin wave restart */
+ SLOT->Cnt = 0;
+ /* set attack */
+ SLOT->evm = ENV_MOD_AR;
+ SLOT->evs = SLOT->evsa;
+ SLOT->evc = EG_AST;
+ SLOT->eve = EG_AED;
+}
+/* ----- key off ----- */
+INLINE void OPL_KEYOFF(OPL_SLOT *SLOT) {
+ if( SLOT->evm > ENV_MOD_RR) {
+ /* set envelope counter from envleope output */
+ SLOT->evm = ENV_MOD_RR;
+ if( !(SLOT->evc & EG_DST) )
+ //SLOT->evc = (ENV_CURVE[SLOT->evc>>ENV_BITS]<<ENV_BITS) + EG_DST;
+ SLOT->evc = EG_DST;
+ SLOT->eve = EG_DED;
+ SLOT->evs = SLOT->evsr;
+ }
+}
+
+/* ---------- calcrate Envelope Generator & Phase Generator ---------- */
+/* return : envelope output */
+INLINE guint32 OPL_CALC_SLOT(OPL_SLOT *SLOT) {
+ /* calcrate envelope generator */
+ if((SLOT->evc += SLOT->evs) >= SLOT->eve) {
+ switch( SLOT->evm ){
+ case ENV_MOD_AR: /* ATTACK -> DECAY1 */
+ /* next DR */
+ SLOT->evm = ENV_MOD_DR;
+ SLOT->evc = EG_DST;
+ SLOT->eve = SLOT->SL;
+ SLOT->evs = SLOT->evsd;
+ break;
+ case ENV_MOD_DR: /* DECAY -> SL or RR */
+ SLOT->evc = SLOT->SL;
+ SLOT->eve = EG_DED;
+ if(SLOT->eg_typ) {
+ SLOT->evs = 0;
+ } else {
+ SLOT->evm = ENV_MOD_RR;
+ SLOT->evs = SLOT->evsr;
+ }
+ break;
+ case ENV_MOD_RR: /* RR -> OFF */
+ SLOT->evc = EG_OFF;
+ SLOT->eve = EG_OFF + 1;
+ SLOT->evs = 0;
+ break;
+ }
+ }
+ /* calcrate envelope */
+ return SLOT->TLL + ENV_CURVE[SLOT->evc>>ENV_BITS] + (SLOT->ams ? ams : 0);
+}
+
+/* set algorythm connection */
+static void set_algorythm(OPL_CH *CH) {
+ int *carrier = &outd[0];
+ CH->connect1 = CH->CON ? carrier : &feedback2;
+ CH->connect2 = carrier;
+}
+
+/* ---------- frequency counter for operater update ---------- */
+INLINE void CALC_FCSLOT(OPL_CH *CH, OPL_SLOT *SLOT) {
+ int ksr;
+
+ /* frequency step counter */
+ SLOT->Incr = CH->fc * SLOT->mul;
+ ksr = CH->kcode >> SLOT->KSR;
+
+ if( SLOT->ksr != ksr )
+ {
+ SLOT->ksr = ksr;
+ /* attack , decay rate recalcration */
+ SLOT->evsa = SLOT->AR[ksr];
+ SLOT->evsd = SLOT->DR[ksr];
+ SLOT->evsr = SLOT->RR[ksr];
+ }
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+}
+
+/* set multi,am,vib,EG-TYP,KSR,mul */
+INLINE void set_mul(FM_OPL *OPL, int slot, int v) {
+ OPL_CH *CH = &OPL->P_CH[slot / 2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot & 1];
+
+ SLOT->mul = MUL_TABLE[v & 0x0f];
+ SLOT->KSR = (v & 0x10) ? 0 : 2;
+ SLOT->eg_typ = (v & 0x20) >> 5;
+ SLOT->vib = (v & 0x40);
+ SLOT->ams = (v & 0x80);
+ CALC_FCSLOT(CH, SLOT);
+}
+
+/* set ksl & tl */
+INLINE void set_ksl_tl(FM_OPL *OPL, int slot, int v) {
+ OPL_CH *CH = &OPL->P_CH[slot / 2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot & 1];
+ int ksl = v >> 6; /* 0 / 1.5 / 3 / 6 db/OCT */
+
+ SLOT->ksl = ksl ? 3-ksl : 31;
+ SLOT->TL = (int)((v & 0x3f) * (0.75 / EG_STEP)); /* 0.75db step */
+
+ if(!(OPL->mode & 0x80)) { /* not CSM latch total level */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base >> SLOT->ksl);
+ }
+}
+
+/* set attack rate & decay rate */
+INLINE void set_ar_dr(FM_OPL *OPL, int slot, int v) {
+ OPL_CH *CH = &OPL->P_CH[slot / 2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot & 1];
+ int ar = v >> 4;
+ int dr = v & 0x0f;
+
+ SLOT->AR = ar ? &OPL->AR_TABLE[ar << 2] : RATE_0;
+ SLOT->evsa = SLOT->AR[SLOT->ksr];
+ if(SLOT->evm == ENV_MOD_AR)
+ SLOT->evs = SLOT->evsa;
+
+ SLOT->DR = dr ? &OPL->DR_TABLE[dr<<2] : RATE_0;
+ SLOT->evsd = SLOT->DR[SLOT->ksr];
+ if(SLOT->evm == ENV_MOD_DR)
+ SLOT->evs = SLOT->evsd;
+}
+
+/* set sustain level & release rate */
+INLINE void set_sl_rr(FM_OPL *OPL, int slot, int v) {
+ OPL_CH *CH = &OPL->P_CH[slot / 2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot & 1];
+ int sl = v >> 4;
+ int rr = v & 0x0f;
+
+ SLOT->SL = SL_TABLE[sl];
+ if(SLOT->evm == ENV_MOD_DR)
+ SLOT->eve = SLOT->SL;
+ SLOT->RR = &OPL->DR_TABLE[rr<<2];
+ SLOT->evsr = SLOT->RR[SLOT->ksr];
+ if(SLOT->evm == ENV_MOD_RR)
+ SLOT->evs = SLOT->evsr;
+}
+
+/* operator output calcrator */
+#define OP_OUT(slot,env,con) slot->wavetable[((slot->Cnt + con) / (0x1000000 / SIN_ENT)) & (SIN_ENT-1)][env]
+/* ---------- calcrate one of channel ---------- */
+INLINE void OPL_CALC_CH(OPL_CH *CH) {
+ guint32 env_out;
+ OPL_SLOT *SLOT;
+
+ feedback2 = 0;
+ /* SLOT 1 */
+ SLOT = &CH->SLOT[SLOT1];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if(env_out < (guint32)(EG_ENT - 1)) {
+ /* PG */
+ if(SLOT->vib)
+ SLOT->Cnt += (SLOT->Incr * vib / VIB_RATE);
+ else
+ SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ if(CH->FB) {
+ int feedback1 = (CH->op1_out[0] + CH->op1_out[1]) >> CH->FB;
+ CH->op1_out[1] = CH->op1_out[0];
+ *CH->connect1 += CH->op1_out[0] = OP_OUT(SLOT, env_out, feedback1);
+ }
+ else {
+ *CH->connect1 += OP_OUT(SLOT, env_out, 0);
+ }
+ }else {
+ CH->op1_out[1] = CH->op1_out[0];
+ CH->op1_out[0] = 0;
+ }
+ /* SLOT 2 */
+ SLOT = &CH->SLOT[SLOT2];
+ env_out=OPL_CALC_SLOT(SLOT);
+ if(env_out < (guint32)(EG_ENT - 1)) {
+ /* PG */
+ if(SLOT->vib)
+ SLOT->Cnt += (SLOT->Incr * vib / VIB_RATE);
+ else
+ SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ outd[0] += OP_OUT(SLOT, env_out, feedback2);
+ }
+}
+
+/* ---------- calcrate rythm block ---------- */
+#define WHITE_NOISE_db 6.0
+INLINE void OPL_CALC_RH(OPL_CH *CH) {
+ guint32 env_tam, env_sd, env_top, env_hh;
+ int whitenoise = (int)((rand()&1) * (WHITE_NOISE_db / EG_STEP));
+ int tone8;
+
+ OPL_SLOT *SLOT;
+ int env_out;
+
+ /* BD : same as FM serial mode and output level is large */
+ feedback2 = 0;
+ /* SLOT 1 */
+ SLOT = &CH[6].SLOT[SLOT1];
+ env_out = OPL_CALC_SLOT(SLOT);
+ if(env_out < EG_ENT-1) {
+ /* PG */
+ if(SLOT->vib)
+ SLOT->Cnt += (SLOT->Incr * vib / VIB_RATE);
+ else
+ SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ if(CH[6].FB) {
+ int feedback1 = (CH[6].op1_out[0] + CH[6].op1_out[1]) >> CH[6].FB;
+ CH[6].op1_out[1] = CH[6].op1_out[0];
+ feedback2 = CH[6].op1_out[0] = OP_OUT(SLOT, env_out, feedback1);
+ }
+ else {
+ feedback2 = OP_OUT(SLOT, env_out, 0);
+ }
+ }else {
+ feedback2 = 0;
+ CH[6].op1_out[1] = CH[6].op1_out[0];
+ CH[6].op1_out[0] = 0;
+ }
+ /* SLOT 2 */
+ SLOT = &CH[6].SLOT[SLOT2];
+ env_out = OPL_CALC_SLOT(SLOT);
+ if(env_out < EG_ENT-1) {
+ /* PG */
+ if(SLOT->vib)
+ SLOT->Cnt += (SLOT->Incr * vib / VIB_RATE);
+ else
+ SLOT->Cnt += SLOT->Incr;
+ /* connectoion */
+ outd[0] += OP_OUT(SLOT, env_out, feedback2) * 2;
+ }
+
+ // SD (17) = mul14[fnum7] + white noise
+ // TAM (15) = mul15[fnum8]
+ // TOP (18) = fnum6(mul18[fnum8]+whitenoise)
+ // HH (14) = fnum7(mul18[fnum8]+whitenoise) + white noise
+ env_sd = OPL_CALC_SLOT(SLOT7_2) + whitenoise;
+ env_tam =OPL_CALC_SLOT(SLOT8_1);
+ env_top = OPL_CALC_SLOT(SLOT8_2);
+ env_hh = OPL_CALC_SLOT(SLOT7_1) + whitenoise;
+
+ /* PG */
+ if(SLOT7_1->vib)
+ SLOT7_1->Cnt += (2 * SLOT7_1->Incr * vib / VIB_RATE);
+ else
+ SLOT7_1->Cnt += 2 * SLOT7_1->Incr;
+ if(SLOT7_2->vib)
+ SLOT7_2->Cnt += ((CH[7].fc * 8) * vib / VIB_RATE);
+ else
+ SLOT7_2->Cnt += (CH[7].fc * 8);
+ if(SLOT8_1->vib)
+ SLOT8_1->Cnt += (SLOT8_1->Incr * vib / VIB_RATE);
+ else
+ SLOT8_1->Cnt += SLOT8_1->Incr;
+ if(SLOT8_2->vib)
+ SLOT8_2->Cnt += ((CH[8].fc * 48) * vib / VIB_RATE);
+ else
+ SLOT8_2->Cnt += (CH[8].fc * 48);
+
+ tone8 = OP_OUT(SLOT8_2,whitenoise,0 );
+
+ /* SD */
+ if(env_sd < (guint32)(EG_ENT - 1))
+ outd[0] += OP_OUT(SLOT7_1, env_sd, 0) * 8;
+ /* TAM */
+ if(env_tam < (guint32)(EG_ENT - 1))
+ outd[0] += OP_OUT(SLOT8_1, env_tam, 0) * 2;
+ /* TOP-CY */
+ if(env_top < (guint32)(EG_ENT - 1))
+ outd[0] += OP_OUT(SLOT7_2, env_top, tone8) * 2;
+ /* HH */
+ if(env_hh < (guint32)(EG_ENT-1))
+ outd[0] += OP_OUT(SLOT7_2, env_hh, tone8) * 2;
+}
+
+/* ----------- initialize time tabls ----------- */
+static void init_timetables(FM_OPL *OPL, int ARRATE, int DRRATE) {
+ int i;
+ double rate;
+
+ /* make attack rate & decay rate tables */
+ for (i = 0; i < 4; i++)
+ OPL->AR_TABLE[i] = OPL->DR_TABLE[i] = 0;
+ for (i = 4; i <= 60; i++){
+ rate = OPL->freqbase; /* frequency rate */
+ if(i < 60)
+ rate *= 1.0 + (i & 3) * 0.25; /* b0-1 : x1 , x1.25 , x1.5 , x1.75 */
+ rate *= 1 << ((i >> 2) - 1); /* b2-5 : shift bit */
+ rate *= (double)(EG_ENT << ENV_BITS);
+ OPL->AR_TABLE[i] = (int)(rate / ARRATE);
+ OPL->DR_TABLE[i] = (int)(rate / DRRATE);
+ }
+ for (i = 60; i < 75; i++) {
+ OPL->AR_TABLE[i] = EG_AED-1;
+ OPL->DR_TABLE[i] = OPL->DR_TABLE[60];
+ }
+}
+
+/* ---------- generic table initialize ---------- */
+static int OPLOpenTable(void) {
+ int s,t;
+ double rate;
+ int i,j;
+ double pom;
+
+ /* allocate dynamic tables */
+ if((TL_TABLE = (int *)malloc(TL_MAX * 2 * sizeof(int))) == NULL)
+ return 0;
+ if((SIN_TABLE = (int **)malloc(SIN_ENT * 4 * sizeof(int *))) == NULL) {
+ free(TL_TABLE);
+ return 0;
+ }
+ if((AMS_TABLE = (int *)malloc(AMS_ENT * 2 * sizeof(int))) == NULL) {
+ free(TL_TABLE);
+ free(SIN_TABLE);
+ return 0;
+ }
+ if((VIB_TABLE = (int *)malloc(VIB_ENT * 2 * sizeof(int))) == NULL) {
+ free(TL_TABLE);
+ free(SIN_TABLE);
+ free(AMS_TABLE);
+ return 0;
+ }
+ /* make total level table */
+ for (t = 0; t < EG_ENT - 1 ; t++){
+ rate = ((1 << TL_BITS) - 1) / pow(10.0, EG_STEP * t / 20); /* dB -> voltage */
+ TL_TABLE[ t] = (int)rate;
+ TL_TABLE[TL_MAX + t] = -TL_TABLE[t];
+ }
+ /* fill volume off area */
+ for (t = EG_ENT - 1; t < TL_MAX; t++){
+ TL_TABLE[t] = TL_TABLE[TL_MAX + t] = 0;
+ }
+
+ /* make sinwave table (total level offet) */
+ /* degree 0 = degree 180 = off */
+ SIN_TABLE[0] = SIN_TABLE[SIN_ENT /2 ] = &TL_TABLE[EG_ENT - 1];
+ for (s = 1;s <= SIN_ENT / 4; s++){
+ pom = sin(2 * PI * s / SIN_ENT); /* sin */
+ pom = 20 * log10(1 / pom); /* decibel */
+ j = (int)(pom / EG_STEP); /* TL_TABLE steps */
+
+ /* degree 0 - 90 , degree 180 - 90 : plus section */
+ SIN_TABLE[ s] = SIN_TABLE[SIN_ENT / 2 - s] = &TL_TABLE[j];
+ /* degree 180 - 270 , degree 360 - 270 : minus section */
+ SIN_TABLE[SIN_ENT / 2 + s] = SIN_TABLE[SIN_ENT - s] = &TL_TABLE[TL_MAX + j];
+ }
+ for (s = 0;s < SIN_ENT; s++) {
+ SIN_TABLE[SIN_ENT * 1 + s] = s < (SIN_ENT / 2) ? SIN_TABLE[s] : &TL_TABLE[EG_ENT];
+ SIN_TABLE[SIN_ENT * 2 + s] = SIN_TABLE[s % (SIN_ENT / 2)];
+ SIN_TABLE[SIN_ENT * 3 + s] = (s / (SIN_ENT / 4)) & 1 ? &TL_TABLE[EG_ENT] : SIN_TABLE[SIN_ENT * 2 + s];
+ }
+
+ /* envelope counter -> envelope output table */
+ for (i=0; i < EG_ENT; i++) {
+ /* ATTACK curve */
+ pom = pow(((double)(EG_ENT - 1 - i) / EG_ENT), 8) * EG_ENT;
+ /* if( pom >= EG_ENT ) pom = EG_ENT-1; */
+ ENV_CURVE[i] = (int)pom;
+ /* DECAY ,RELEASE curve */
+ ENV_CURVE[(EG_DST >> ENV_BITS) + i]= i;
+ }
+ /* off */
+ ENV_CURVE[EG_OFF >> ENV_BITS]= EG_ENT - 1;
+ /* make LFO ams table */
+ for (i=0; i < AMS_ENT; i++) {
+ pom = (1.0 + sin(2 * PI * i / AMS_ENT)) / 2; /* sin */
+ AMS_TABLE[i] = (int)((1.0 / EG_STEP) * pom); /* 1dB */
+ AMS_TABLE[AMS_ENT + i] = (int)((4.8 / EG_STEP) * pom); /* 4.8dB */
+ }
+ /* make LFO vibrate table */
+ for (i=0; i < VIB_ENT; i++) {
+ /* 100cent = 1seminote = 6% ?? */
+ pom = (double)VIB_RATE * 0.06 * sin(2 * PI * i / VIB_ENT); /* +-100sect step */
+ VIB_TABLE[i] = (int)(VIB_RATE + (pom * 0.07)); /* +- 7cent */
+ VIB_TABLE[VIB_ENT + i] = (int)(VIB_RATE + (pom * 0.14)); /* +-14cent */
+ }
+ return 1;
+}
+
+static void OPLCloseTable(void) {
+ free(TL_TABLE);
+ free(SIN_TABLE);
+ free(AMS_TABLE);
+ free(VIB_TABLE);
+}
+
+/* CSM Key Controll */
+INLINE void CSMKeyControll(OPL_CH *CH) {
+ OPL_SLOT *slot1 = &CH->SLOT[SLOT1];
+ OPL_SLOT *slot2 = &CH->SLOT[SLOT2];
+ /* all key off */
+ OPL_KEYOFF(slot1);
+ OPL_KEYOFF(slot2);
+ /* total level latch */
+ slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl);
+ slot1->TLL = slot1->TL + (CH->ksl_base>>slot1->ksl);
+ /* key on */
+ CH->op1_out[0] = CH->op1_out[1] = 0;
+ OPL_KEYON(slot1);
+ OPL_KEYON(slot2);
+}
+
+/* ---------- opl initialize ---------- */
+static void OPL_initalize(FM_OPL *OPL) {
+ int fn;
+
+ /* frequency base */
+ OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / OPL->rate) / 72 : 0;
+ /* Timer base time */
+ OPL->TimerBase = 1.0/((double)OPL->clock / 72.0 );
+ /* make time tables */
+ init_timetables(OPL, OPL_ARRATE, OPL_DRRATE);
+ /* make fnumber -> increment counter table */
+ for( fn=0; fn < 1024; fn++) {
+ OPL->FN_TABLE[fn] = (guint32)(OPL->freqbase * fn * FREQ_RATE * (1<<7) / 2);
+ }
+ /* LFO freq.table */
+ OPL->amsIncr = (int)(OPL->rate ? (double)AMS_ENT * (1 << AMS_SHIFT) / OPL->rate * 3.7 * ((double)OPL->clock/3600000) : 0);
+ OPL->vibIncr = (int)(OPL->rate ? (double)VIB_ENT * (1 << VIB_SHIFT) / OPL->rate * 6.4 * ((double)OPL->clock/3600000) : 0);
+}
+
+/* ---------- write a OPL registers ---------- */
+void OPLWriteReg(FM_OPL *OPL, int r, int v) {
+ OPL_CH *CH;
+ int slot;
+ guint32 block_fnum;
+
+ switch(r & 0xe0) {
+ case 0x00: /* 00-1f:controll */
+ switch(r & 0x1f) {
+ case 0x01:
+ /* wave selector enable */
+ if(OPL->type&OPL_TYPE_WAVESEL) {
+ OPL->wavesel = v & 0x20;
+ if(!OPL->wavesel) {
+ /* preset compatible mode */
+ int c;
+ for(c=0; c<OPL->max_ch; c++) {
+ OPL->P_CH[c].SLOT[SLOT1].wavetable = &SIN_TABLE[0];
+ OPL->P_CH[c].SLOT[SLOT2].wavetable = &SIN_TABLE[0];
+ }
+ }
+ }
+ return;
+ case 0x02: /* Timer 1 */
+ OPL->T[0] = (256-v) * 4;
+ break;
+ case 0x03: /* Timer 2 */
+ OPL->T[1] = (256-v) * 16;
+ return;
+ case 0x04: /* IRQ clear / mask and Timer enable */
+ if(v & 0x80) { /* IRQ flag clear */
+ OPL_STATUS_RESET(OPL, 0x7f);
+ }
+ else { /* set IRQ mask ,timer enable*/
+ guint8 st1 = v & 1;
+ guint8 st2 = (v >> 1) & 1;
+ /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */
+ OPL_STATUS_RESET(OPL, v & 0x78);
+ OPL_STATUSMASK_SET(OPL,((~v) & 0x78) | 0x01);
+ /* timer 2 */
+ if(OPL->st[1] != st2) {
+ double interval = st2 ? (double)OPL->T[1] * OPL->TimerBase : 0.0;
+ OPL->st[1] = st2;
+ if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam + 1, interval);
+ }
+ /* timer 1 */
+ if(OPL->st[0] != st1) {
+ double interval = st1 ? (double)OPL->T[0] * OPL->TimerBase : 0.0;
+ OPL->st[0] = st1;
+ if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam + 0, interval);
+ }
+ }
+ return;
+ }
+ break;
+ case 0x20: /* am,vib,ksr,eg type,mul */
+ slot = slot_array[r&0x1f];
+ if(slot == -1)
+ return;
+ set_mul(OPL,slot,v);
+ return;
+ case 0x40:
+ slot = slot_array[r&0x1f];
+ if(slot == -1)
+ return;
+ set_ksl_tl(OPL,slot,v);
+ return;
+ case 0x60:
+ slot = slot_array[r&0x1f];
+ if(slot == -1)
+ return;
+ set_ar_dr(OPL,slot,v);
+ return;
+ case 0x80:
+ slot = slot_array[r&0x1f];
+ if(slot == -1)
+ return;
+ set_sl_rr(OPL,slot,v);
+ return;
+ case 0xa0:
+ switch(r) {
+ case 0xbd:
+ /* amsep,vibdep,r,bd,sd,tom,tc,hh */
+ {
+ guint8 rkey = OPL->rythm ^ v;
+ OPL->ams_table = &AMS_TABLE[v & 0x80 ? AMS_ENT : 0];
+ OPL->vib_table = &VIB_TABLE[v & 0x40 ? VIB_ENT : 0];
+ OPL->rythm = v & 0x3f;
+ if(OPL->rythm & 0x20) {
+ /* BD key on/off */
+ if(rkey & 0x10) {
+ if(v & 0x10) {
+ OPL->P_CH[6].op1_out[0] = OPL->P_CH[6].op1_out[1] = 0;
+ OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT1]);
+ OPL_KEYON(&OPL->P_CH[6].SLOT[SLOT2]);
+ }
+ else {
+ OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1]);
+ OPL_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2]);
+ }
+ }
+ /* SD key on/off */
+ if(rkey & 0x08) {
+ if(v & 0x08)
+ OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT2]);
+ else
+ OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2]);
+ }/* TAM key on/off */
+ if(rkey & 0x04) {
+ if(v & 0x04)
+ OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT1]);
+ else
+ OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1]);
+ }
+ /* TOP-CY key on/off */
+ if(rkey & 0x02) {
+ if(v & 0x02)
+ OPL_KEYON(&OPL->P_CH[8].SLOT[SLOT2]);
+ else
+ OPL_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2]);
+ }
+ /* HH key on/off */
+ if(rkey & 0x01) {
+ if(v & 0x01)
+ OPL_KEYON(&OPL->P_CH[7].SLOT[SLOT1]);
+ else
+ OPL_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1]);
+ }
+ }
+ }
+ return;
+ }
+ /* keyon,block,fnum */
+ if((r & 0x0f) >= ADLIB_VOICES)
+ return;
+ CH = &OPL->P_CH[r & 0x0f];
+ if(!(r&0x10)) { /* a0-a8 */
+ block_fnum = (CH->block_fnum & 0x1f00) | v;
+ }
+ else { /* b0-b8 */
+ int keyon = (v >> 5) & 1;
+ block_fnum = ((v & 0x1f) << 8) | (CH->block_fnum & 0xff);
+ if(CH->keyon != keyon) {
+ if((CH->keyon=keyon)) {
+ CH->op1_out[0] = CH->op1_out[1] = 0;
+ OPL_KEYON(&CH->SLOT[SLOT1]);
+ OPL_KEYON(&CH->SLOT[SLOT2]);
+ }
+ else {
+ OPL_KEYOFF(&CH->SLOT[SLOT1]);
+ OPL_KEYOFF(&CH->SLOT[SLOT2]);
+ }
+ }
+ }
+ /* update */
+ if(CH->block_fnum != block_fnum) {
+ int blockRv = 7 - (block_fnum >> 10);
+ int fnum = block_fnum & 0x3ff;
+ CH->block_fnum = block_fnum;
+ CH->ksl_base = KSL_TABLE[block_fnum >> 6];
+ CH->fc = OPL->FN_TABLE[fnum] >> blockRv;
+ CH->kcode = CH->block_fnum >> 9;
+ if((OPL->mode & 0x40) && CH->block_fnum & 0x100)
+ CH->kcode |=1;
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ }
+ return;
+ case 0xc0:
+ /* FB,C */
+ if((r & 0x0f) >= ADLIB_VOICES)
+ return;
+ CH = &OPL->P_CH[r&0x0f];
+ {
+ int feedback = (v >> 1) & 7;
+ CH->FB = feedback ? (8 + 1) - feedback : 0;
+ CH->CON = v & 1;
+ set_algorythm(CH);
+ }
+ return;
+ case 0xe0: /* wave type */
+ slot = slot_array[r & 0x1f];
+ if(slot == -1)
+ return;
+ CH = &OPL->P_CH[slot / 2];
+ if(OPL->wavesel) {
+ CH->SLOT[slot&1].wavetable = &SIN_TABLE[(v & 0x03) * SIN_ENT];
+ }
+ return;
+ }
+}
+
+/* lock/unlock for common table */
+static int OPL_LockTable(void)
+{
+ num_lock++;
+ if(num_lock>1)
+ return 0;
+ /* first time */
+ cur_chip = NULL;
+ /* allocate total level table (128kb space) */
+ if(!OPLOpenTable()) {
+ num_lock--;
+ return -1;
+ }
+ return 0;
+}
+
+static void OPL_UnLockTable(void) {
+ if(num_lock)
+ num_lock--;
+ if(num_lock)
+ return;
+ /* last time */
+ cur_chip = NULL;
+ OPLCloseTable();
+}
+
+/*******************************************************************************/
+/* YM3812 local section */
+/*******************************************************************************/
+
+/* ---------- update one of chip ----------- */
+void YM3812UpdateOne(FM_OPL *OPL, gint16 *buffer, int length, int interleave) {
+ int i;
+ int data;
+ gint16 *buf = buffer;
+ guint32 amsCnt = OPL->amsCnt;
+ guint32 vibCnt = OPL->vibCnt;
+ guint8 rythm = OPL->rythm & 0x20;
+ OPL_CH *CH, *R_CH;
+
+ if((void *)OPL != cur_chip){
+ cur_chip = (void *)OPL;
+ /* channel pointers */
+ S_CH = OPL->P_CH;
+ E_CH = &S_CH[9];
+ /* rythm slot */
+ SLOT7_1 = &S_CH[7].SLOT[SLOT1];
+ SLOT7_2 = &S_CH[7].SLOT[SLOT2];
+ SLOT8_1 = &S_CH[8].SLOT[SLOT1];
+ SLOT8_2 = &S_CH[8].SLOT[SLOT2];
+ /* LFO state */
+ amsIncr = OPL->amsIncr;
+ vibIncr = OPL->vibIncr;
+ ams_table = OPL->ams_table;
+ vib_table = OPL->vib_table;
+ }
+ R_CH = rythm ? &S_CH[6] : E_CH;
+ for(i = 0; i < length; i++) {
+ /* channel A channel B channel C */
+ /* LFO */
+ ams = ams_table[(amsCnt += amsIncr) >> AMS_SHIFT];
+ vib = vib_table[(vibCnt += vibIncr) >> VIB_SHIFT];
+ outd[0] = 0;
+ /* FM part */
+ for(CH=S_CH; CH < R_CH; CH++)
+ OPL_CALC_CH(CH);
+ /* Rythn part */
+ if(rythm)
+ OPL_CALC_RH(S_CH);
+#ifdef TWELVE_VOICE
+ for(CH=&S_CH[9]; CH < &S_CH[ADLIB_VOICES]; CH++)
+ OPL_CALC_CH(CH);
+#endif
+ /* limit check */
+ data = Limit(outd[0], OPL_MAXOUT, OPL_MINOUT);
+ /* store to sound buffer */
+ buf[i << interleave] = data >> OPL_OUTSB;
+ }
+
+ OPL->amsCnt = amsCnt;
+ OPL->vibCnt = vibCnt;
+}
+
+/* ---------- reset a chip ---------- */
+void OPLResetChip(FM_OPL *OPL) {
+ int c,s;
+ int i;
+
+ /* reset chip */
+ OPL->mode = 0; /* normal mode */
+ OPL_STATUS_RESET(OPL, 0x7f);
+ /* reset with register write */
+ OPLWriteReg(OPL, 0x01,0); /* wabesel disable */
+ OPLWriteReg(OPL, 0x02,0); /* Timer1 */
+ OPLWriteReg(OPL, 0x03,0); /* Timer2 */
+ OPLWriteReg(OPL, 0x04,0); /* IRQ mask clear */
+ for(i = 0xff; i >= 0x20; i--)
+ OPLWriteReg(OPL,i,0);
+ /* reset OPerator parameter */
+ for(c = 0; c < OPL->max_ch ;c++ ) {
+ OPL_CH *CH = &OPL->P_CH[c];
+ /* OPL->P_CH[c].PAN = OPN_CENTER; */
+ for(s = 0; s < 2; s++ ) {
+ /* wave table */
+ CH->SLOT[s].wavetable = &SIN_TABLE[0];
+ /* CH->SLOT[s].evm = ENV_MOD_RR; */
+ CH->SLOT[s].evc = EG_OFF;
+ CH->SLOT[s].eve = EG_OFF + 1;
+ CH->SLOT[s].evs = 0;
+ }
+ }
+}
+
+/* ---------- Create a virtual YM3812 ---------- */
+/* 'rate' is sampling rate and 'bufsiz' is the size of the */
+FM_OPL *OPLCreate(int type, int clock, int rate) {
+ char *ptr;
+ FM_OPL *OPL;
+ int state_size;
+#ifdef TWELVE_VOICE
+ int max_ch = 12;
+#else
+ int max_ch = 9; /* normaly 9 channels */
+#endif
+
+ if( OPL_LockTable() == -1)
+ return NULL;
+ /* allocate OPL state space */
+ state_size = sizeof(FM_OPL);
+ state_size += sizeof(OPL_CH) * max_ch;
+
+ /* allocate memory block */
+ ptr = (char *)calloc(state_size, 1);
+ if(ptr == NULL)
+ return NULL;
+
+ /* clear */
+ memset(ptr, 0, state_size);
+ OPL = (FM_OPL *)ptr; ptr += sizeof(FM_OPL);
+ OPL->P_CH = (OPL_CH *)ptr; ptr += sizeof(OPL_CH) * max_ch;
+
+ /* set channel state pointer */
+ OPL->type = type;
+ OPL->clock = clock;
+ OPL->rate = rate;
+ OPL->max_ch = max_ch;
+
+ /* init grobal tables */
+ OPL_initalize(OPL);
+
+ /* reset chip */
+ OPLResetChip(OPL);
+ return OPL;
+}
+
+/* ---------- Destroy one of vietual YM3812 ---------- */
+void OPLDestroy(FM_OPL *OPL) {
+ OPL_UnLockTable();
+ free(OPL);
+}
+
+/* ---------- Option handlers ---------- */
+
+void OPLSetTimerHandler(FM_OPL *OPL, OPL_TIMERHANDLER TimerHandler,int channelOffset) {
+ OPL->TimerHandler = TimerHandler;
+ OPL->TimerParam = channelOffset;
+}
+
+void OPLSetIRQHandler(FM_OPL *OPL, OPL_IRQHANDLER IRQHandler, int param) {
+ OPL->IRQHandler = IRQHandler;
+ OPL->IRQParam = param;
+}
+void OPLSetUpdateHandler(FM_OPL *OPL, OPL_UPDATEHANDLER UpdateHandler,int param) {
+ OPL->UpdateHandler = UpdateHandler;
+ OPL->UpdateParam = param;
+}
+
+/* ---------- YM3812 I/O interface ---------- */
+int OPLWrite(FM_OPL *OPL,int a,int v) {
+ if(!(a & 1)) { /* address port */
+ OPL->address = v & 0xff;
+ }
+ else { /* data port */
+ if(OPL->UpdateHandler)
+ OPL->UpdateHandler(OPL->UpdateParam,0);
+ OPLWriteReg(OPL, OPL->address,v);
+ }
+ return OPL->status >> 7;
+}
+
+unsigned char OPLRead(FM_OPL *OPL,int a) {
+ if(!(a & 1)) { /* status port */
+ return OPL->status & (OPL->statusmask | 0x80);
+ }
+ /* data port */
+ switch(OPL->address) {
+ case 0x05: /* KeyBoard IN */
+ fprintf(stderr, "OPL:read unmapped KEYBOARD port\n");
+ return 0;
+ case 0x19: /* I/O DATA */
+ fprintf(stderr, "OPL:read unmapped I/O port\n");
+ return 0;
+ case 0x1a: /* PCM-DATA */
+ return 0;
+ }
+ return 0;
+}
+
+int OPLTimerOver(FM_OPL *OPL, int c) {
+ if(c) { /* Timer B */
+ OPL_STATUS_SET(OPL, 0x20);
+ }
+ else { /* Timer A */
+ OPL_STATUS_SET(OPL, 0x40);
+ /* CSM mode key,TL controll */
+ if(OPL->mode & 0x80) { /* CSM mode total level latch and auto key on */
+ int ch;
+ if(OPL->UpdateHandler)
+ OPL->UpdateHandler(OPL->UpdateParam,0);
+ for(ch = 0; ch < 9; ch++)
+ CSMKeyControll(&OPL->P_CH[ch]);
+ }
+ }
+ /* reload timer */
+ if (OPL->TimerHandler)
+ (OPL->TimerHandler)(OPL->TimerParam + c, (double)OPL->T[c] * OPL->TimerBase);
+ return OPL->status >> 7;
+}
diff --git a/engines/sci/sfx/softseq/fmopl.h b/engines/sci/sfx/softseq/fmopl.h
new file mode 100644
index 0000000000..5bb1cd80d7
--- /dev/null
+++ b/engines/sci/sfx/softseq/fmopl.h
@@ -0,0 +1,168 @@
+/***************************************************************************
+ fmopl.h Copyright (C) 1999-2000 Tatsuyuki Satoh
+ 2001-2004 The ScummVM project
+ 2002 Solomon Peachy
+ 2004 Walter van Niftrik
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+ LGPL licensed version of MAMEs fmopl (V0.37a modified) by
+ Tatsuyuki Satoh. Included from LGPL'ed AdPlug.
+
+***************************************************************************/
+
+#ifndef FMOPL_H_
+#define FMOPL_H_
+
+#include <scitypes.h>
+
+#define TWELVE_VOICE 1
+#ifdef TWELVE_VOICE
+#define ADLIB_VOICES 12
+#else
+#define ADLIB_VOICES 9
+#endif
+
+enum {
+ FMOPL_ENV_BITS_HQ = 16,
+ FMOPL_ENV_BITS_LQ = 8,
+ FMOPL_EG_ENT_HQ = 4096,
+ FMOPL_EG_ENT_LQ = 128
+};
+
+
+typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec);
+typedef void (*OPL_IRQHANDLER)(int param,int irq);
+typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us);
+
+#define OPL_TYPE_WAVESEL 0x01 /* waveform select */
+
+/* Saving is necessary for member of the 'R' mark for suspend/resume */
+/* ---------- OPL one of slot ---------- */
+typedef struct fm_opl_slot {
+ int TL; /* total level :TL << 8 */
+ int TLL; /* adjusted now TL */
+ guint8 KSR; /* key scale rate :(shift down bit) */
+ int *AR; /* attack rate :&AR_TABLE[AR<<2] */
+ int *DR; /* decay rate :&DR_TABLE[DR<<2] */
+ int SL; /* sustain level :SL_TABLE[SL] */
+ int *RR; /* release rate :&DR_TABLE[RR<<2] */
+ guint8 ksl; /* keyscale level :(shift down bits) */
+ guint8 ksr; /* key scale rate :kcode>>KSR */
+ guint32 mul; /* multiple :ML_TABLE[ML] */
+ guint32 Cnt; /* frequency count */
+ guint32 Incr; /* frequency step */
+
+ /* envelope generator state */
+ guint8 eg_typ;/* envelope type flag */
+ guint8 evm; /* envelope phase */
+ int evc; /* envelope counter */
+ int eve; /* envelope counter end point */
+ int evs; /* envelope counter step */
+ int evsa; /* envelope step for AR :AR[ksr] */
+ int evsd; /* envelope step for DR :DR[ksr] */
+ int evsr; /* envelope step for RR :RR[ksr] */
+
+ /* LFO */
+ guint8 ams; /* ams flag */
+ guint8 vib; /* vibrate flag */
+ /* wave selector */
+ int **wavetable;
+} OPL_SLOT;
+
+/* ---------- OPL one of channel ---------- */
+typedef struct fm_opl_channel {
+ OPL_SLOT SLOT[2];
+ guint8 CON; /* connection type */
+ guint8 FB; /* feed back :(shift down bit)*/
+ int *connect1; /* slot1 output pointer */
+ int *connect2; /* slot2 output pointer */
+ int op1_out[2]; /* slot1 output for selfeedback */
+
+ /* phase generator state */
+ guint32 block_fnum; /* block+fnum */
+ guint8 kcode; /* key code : KeyScaleCode */
+ guint32 fc; /* Freq. Increment base */
+ guint32 ksl_base; /* KeyScaleLevel Base step */
+ guint8 keyon; /* key on/off flag */
+} OPL_CH;
+
+/* OPL state */
+typedef struct fm_opl_f {
+ guint8 type; /* chip type */
+ int clock; /* master clock (Hz) */
+ int rate; /* sampling rate (Hz) */
+ double freqbase; /* frequency base */
+ double TimerBase; /* Timer base time (==sampling time) */
+ guint8 address; /* address register */
+ guint8 status; /* status flag */
+ guint8 statusmask; /* status mask */
+ guint32 mode; /* Reg.08 : CSM , notesel,etc. */
+
+ /* Timer */
+ int T[2]; /* timer counter */
+ guint8 st[2]; /* timer enable */
+
+ /* FM channel slots */
+ OPL_CH *P_CH; /* pointer of CH */
+ int max_ch; /* maximum channel */
+
+ /* Rythm sention */
+ guint8 rythm; /* Rythm mode , key flag */
+
+ /* time tables */
+ int AR_TABLE[75]; /* atttack rate tables */
+ int DR_TABLE[75]; /* decay rate tables */
+ guint32 FN_TABLE[1024];/* fnumber -> increment counter */
+
+ /* LFO */
+ int *ams_table;
+ int *vib_table;
+ int amsCnt;
+ int amsIncr;
+ int vibCnt;
+ int vibIncr;
+
+ /* wave selector enable flag */
+ guint8 wavesel;
+
+ /* external event callback handler */
+ OPL_TIMERHANDLER TimerHandler; /* TIMER handler */
+ int TimerParam; /* TIMER parameter */
+ OPL_IRQHANDLER IRQHandler; /* IRQ handler */
+ int IRQParam; /* IRQ parameter */
+ OPL_UPDATEHANDLER UpdateHandler; /* stream update handler */
+ int UpdateParam; /* stream update parameter */
+} FM_OPL;
+
+/* ---------- Generic interface section ---------- */
+#define OPL_TYPE_YM3526 (0)
+#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL)
+
+void OPLBuildTables(int ENV_BITS_PARAM, int EG_ENT_PARAM);
+
+FM_OPL *OPLCreate(int type, int clock, int rate);
+void OPLDestroy(FM_OPL *OPL);
+void OPLSetTimerHandler(FM_OPL *OPL, OPL_TIMERHANDLER TimerHandler, int channelOffset);
+void OPLSetIRQHandler(FM_OPL *OPL, OPL_IRQHANDLER IRQHandler, int param);
+void OPLSetUpdateHandler(FM_OPL *OPL, OPL_UPDATEHANDLER UpdateHandler, int param);
+
+void OPLResetChip(FM_OPL *OPL);
+int OPLWrite(FM_OPL *OPL, int a, int v);
+unsigned char OPLRead(FM_OPL *OPL, int a);
+int OPLTimerOver(FM_OPL *OPL, int c);
+void OPLWriteReg(FM_OPL *OPL, int r, int v);
+void YM3812UpdateOne(FM_OPL *OPL, gint16 *buffer, int length, int interleave);
+#endif
diff --git a/engines/sci/sfx/softseq/mt32.cpp b/engines/sci/sfx/softseq/mt32.cpp
new file mode 100644
index 0000000000..6b37673c9e
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32.cpp
@@ -0,0 +1,228 @@
+/***************************************************************************
+ Copyright (C) 2008 Walter van Niftrik
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantability,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Walter van Niftrik <w.f.b.w.v.niftrik@stud.tue.nl>
+
+***************************************************************************/
+
+extern "C" {
+#include "../softseq.h"
+#include "../sequencer.h"
+#include "../device.h"
+#include "resource.h"
+#include "sci_memory.h"
+}
+
+#include "mt32/mt32emu.h"
+
+/* FIXME */
+#define FREESCI_GAMEDIR ".freesci"
+
+#define SAMPLE_RATE 32000
+#define CHANNELS SFX_PCM_STEREO_LR
+#define STEREO 1
+
+static MT32Emu::Synth *synth;
+static sfx_sequencer_t *mt32seq;
+static int initializing;
+
+/* MIDI writer */
+
+static int
+mt32_midi_init(struct _midi_writer *self)
+{
+ return SFX_OK;
+}
+
+static int
+mt32_midi_set_option(struct _midi_writer *self, char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+static int
+mt32_midi_write(struct _midi_writer *self, unsigned char *buf, int len)
+{
+ if (buf[0] == 0xf0)
+ synth->playSysex(buf, len);
+ else if (len < 4) {
+ MT32Emu::Bit32u msg;
+ msg = buf[0];
+ if (len > 0)
+ msg |= buf[1] << 8;
+ if (len > 1)
+ msg |= buf[2] << 16;
+ synth->playMsg(msg);
+ }
+ else
+ sciprintf("MT32EMU: Skipping non-sysex message of more than 3 bytes.\n");
+
+ return SFX_OK;
+}
+
+static void
+mt32_midi_delay(struct _midi_writer *self, int ticks)
+{
+}
+
+static void
+mt32_midi_reset_timer(struct _midi_writer *self)
+{
+}
+
+static void
+mt32_midi_close(struct _midi_writer *self)
+{
+}
+
+static midi_writer_t midi_writer_mt32 = {
+ "mt32",
+ mt32_midi_init,
+ mt32_midi_set_option,
+ mt32_midi_write,
+ mt32_midi_delay,
+ NULL,
+ mt32_midi_reset_timer,
+ mt32_midi_close
+};
+
+/* Software sequencer */
+
+static void printDebug(void *userData, const char *fmt, va_list list)
+{
+ if (initializing) {
+ vprintf(fmt, list);
+ printf("\n");
+ }
+}
+
+static void
+mt32_poll(sfx_softseq_t *self, byte *dest, int count)
+{
+ synth->render((MT32Emu::Bit16s *) dest, count);
+}
+
+static int
+mt32_init(sfx_softseq_t *self, byte *data_ptr, int data_length, byte *data2_ptr,
+ int data2_length)
+{
+ MT32Emu::SynthProperties prop;
+ char *home = sci_get_homedir();
+ char *romdir;
+
+ if (!home) {
+ sciprintf("MT32EMU: Couldn't determine home directory.\n");
+ return SFX_ERROR;
+ }
+
+ romdir = (char *) sci_malloc(sizeof(home) + 2 * sizeof(G_DIR_SEPARATOR_S)
+ + sizeof(FREESCI_GAMEDIR) + 1);
+
+ strcpy(romdir, home);
+ strcat(romdir, G_DIR_SEPARATOR_S);
+ strcat(romdir, FREESCI_GAMEDIR);
+ strcat(romdir, G_DIR_SEPARATOR_S);
+
+ mt32seq = sfx_find_sequencer("MT32");
+ if (!mt32seq) {
+ sciprintf("MT32EMU: Unable to find MT32 sequencer.\n");
+ return SFX_ERROR;
+ }
+
+ prop.sampleRate = SAMPLE_RATE;
+ prop.useReverb = true;
+ prop.useDefaultReverb = true;
+ prop.baseDir = romdir;
+ prop.userData = NULL;
+ prop.report = NULL;
+ prop.printDebug = printDebug;
+ prop.openFile = NULL;
+ prop.closeFile = NULL;
+
+ initializing = 1;
+ synth = new MT32Emu::Synth();
+ if (!synth->open(prop))
+ return SFX_ERROR;
+ initializing = 0;
+
+ mt32seq->open(data_length, data_ptr, data2_length, data2_ptr,
+ &midi_writer_mt32);
+
+ free(romdir);
+
+ return SFX_OK;
+}
+
+static void
+mt32_exit(sfx_softseq_t *self)
+{
+ synth->close();
+ delete synth;
+
+ mt32seq->close();
+}
+
+static void
+mt32_allstop(sfx_softseq_t *self)
+{
+ if (mt32seq->allstop)
+ mt32seq->allstop();
+}
+
+static void
+mt32_volume(sfx_softseq_t *self, int volume)
+{
+ if (mt32seq->volume)
+ mt32seq->volume(volume / 2); /* High volume causes clipping. */
+}
+
+static int
+mt32_set_option(sfx_softseq_t *self, char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+static void
+mt32_event(sfx_softseq_t *self, byte cmd, int argc, byte *argv)
+{
+ mt32seq->event(cmd, argc, argv);
+}
+
+sfx_softseq_t sfx_softseq_mt32 = {
+ "mt32emu",
+ "0.1",
+ mt32_set_option,
+ mt32_init,
+ mt32_exit,
+ mt32_volume,
+ mt32_event,
+ mt32_poll,
+ mt32_allstop,
+ NULL,
+ 001, /* patch.001 */
+ SFX_SEQ_PATCHFILE_NONE,
+ 0x01, /* playflag */
+ 1, /* do play channel 9 */
+ 32, /* Max polypgony */
+ {SAMPLE_RATE, CHANNELS, SFX_PCM_FORMAT_S16_NATIVE}
+};
diff --git a/engines/sci/sfx/softseq/mt32/Makefile.am b/engines/sci/sfx/softseq/mt32/Makefile.am
new file mode 100644
index 0000000000..5d2ffa31c8
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/Makefile.am
@@ -0,0 +1,5 @@
+noinst_HEADERS = freeverb.h mt32emu.h part.h partialManager.h synth.h \
+i386.h mt32_file.h partial.h structures.h tables.h
+noinst_LIBRARIES = libmt32emu.a
+libmt32emu_a_SOURCES = freeverb.cpp mt32_file.cpp partial.cpp synth.cpp \
+i386.cpp part.cpp partialManager.cpp tables.cpp
diff --git a/engines/sci/sfx/softseq/mt32/freeverb.cpp b/engines/sci/sfx/softseq/mt32/freeverb.cpp
new file mode 100644
index 0000000000..1c3aab0494
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/freeverb.cpp
@@ -0,0 +1,310 @@
+/* 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$
+ *
+ */
+
+// Comb filter implementation
+//
+// Written by
+// http://www.dreampoint.co.uk
+// This code is public domain
+
+#include "freeverb.h"
+
+comb::comb() {
+ filterstore = 0;
+ bufidx = 0;
+}
+
+void comb::setbuffer(float *buf, int size) {
+ buffer = buf;
+ bufsize = size;
+}
+
+void comb::mute() {
+ for (int i = 0; i < bufsize; i++)
+ buffer[i] = 0;
+}
+
+void comb::setdamp(float val) {
+ damp1 = val;
+ damp2 = 1 - val;
+}
+
+float comb::getdamp() {
+ return damp1;
+}
+
+void comb::setfeedback(float val) {
+ feedback = val;
+}
+
+float comb::getfeedback() {
+ return feedback;
+}
+
+// Allpass filter implementation
+
+allpass::allpass() {
+ bufidx = 0;
+}
+
+void allpass::setbuffer(float *buf, int size) {
+ buffer = buf;
+ bufsize = size;
+}
+
+void allpass::mute() {
+ for (int i = 0; i < bufsize; i++)
+ buffer[i] = 0;
+}
+
+void allpass::setfeedback(float val) {
+ feedback = val;
+}
+
+float allpass::getfeedback() {
+ return feedback;
+}
+
+// Reverb model implementation
+
+revmodel::revmodel() {
+ // Tie the components to their buffers
+ combL[0].setbuffer(bufcombL1,combtuningL1);
+ combR[0].setbuffer(bufcombR1,combtuningR1);
+ combL[1].setbuffer(bufcombL2,combtuningL2);
+ combR[1].setbuffer(bufcombR2,combtuningR2);
+ combL[2].setbuffer(bufcombL3,combtuningL3);
+ combR[2].setbuffer(bufcombR3,combtuningR3);
+ combL[3].setbuffer(bufcombL4,combtuningL4);
+ combR[3].setbuffer(bufcombR4,combtuningR4);
+ combL[4].setbuffer(bufcombL5,combtuningL5);
+ combR[4].setbuffer(bufcombR5,combtuningR5);
+ combL[5].setbuffer(bufcombL6,combtuningL6);
+ combR[5].setbuffer(bufcombR6,combtuningR6);
+ combL[6].setbuffer(bufcombL7,combtuningL7);
+ combR[6].setbuffer(bufcombR7,combtuningR7);
+ combL[7].setbuffer(bufcombL8,combtuningL8);
+ combR[7].setbuffer(bufcombR8,combtuningR8);
+ allpassL[0].setbuffer(bufallpassL1,allpasstuningL1);
+ allpassR[0].setbuffer(bufallpassR1,allpasstuningR1);
+ allpassL[1].setbuffer(bufallpassL2,allpasstuningL2);
+ allpassR[1].setbuffer(bufallpassR2,allpasstuningR2);
+ allpassL[2].setbuffer(bufallpassL3,allpasstuningL3);
+ allpassR[2].setbuffer(bufallpassR3,allpasstuningR3);
+ allpassL[3].setbuffer(bufallpassL4,allpasstuningL4);
+ allpassR[3].setbuffer(bufallpassR4,allpasstuningR4);
+
+ // Set default values
+ allpassL[0].setfeedback(0.5f);
+ allpassR[0].setfeedback(0.5f);
+ allpassL[1].setfeedback(0.5f);
+ allpassR[1].setfeedback(0.5f);
+ allpassL[2].setfeedback(0.5f);
+ allpassR[2].setfeedback(0.5f);
+ allpassL[3].setfeedback(0.5f);
+ allpassR[3].setfeedback(0.5f);
+ setwet(initialwet);
+ setroomsize(initialroom);
+ setdry(initialdry);
+ setdamp(initialdamp);
+ setwidth(initialwidth);
+ setmode(initialmode);
+
+ // Buffer will be full of rubbish - so we MUST mute them
+ mute();
+}
+
+void revmodel::mute() {
+ int i;
+
+ if (getmode() >= freezemode)
+ return;
+
+ for (i = 0; i < numcombs; i++) {
+ combL[i].mute();
+ combR[i].mute();
+ }
+
+ for (i = 0; i < numallpasses; i++) {
+ allpassL[i].mute();
+ allpassR[i].mute();
+ }
+}
+
+void revmodel::processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip) {
+ float outL, outR, input;
+
+ while (numsamples-- > 0) {
+ int i;
+
+ outL = outR = 0;
+ input = (*inputL + *inputR) * gain;
+
+ // Accumulate comb filters in parallel
+ for (i = 0; i < numcombs; i++) {
+ outL += combL[i].process(input);
+ outR += combR[i].process(input);
+ }
+
+ // Feed through allpasses in series
+ for (i = 0; i < numallpasses; i++) {
+ outL = allpassL[i].process(outL);
+ outR = allpassR[i].process(outR);
+ }
+
+ // Calculate output REPLACING anything already there
+ *outputL = outL * wet1 + outR * wet2 + *inputL * dry;
+ *outputR = outR * wet1 + outL * wet2 + *inputR * dry;
+
+ // Increment sample pointers, allowing for interleave (if any)
+ inputL += skip;
+ inputR += skip;
+ outputL += skip;
+ outputR += skip;
+ }
+}
+
+void revmodel::processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip) {
+ float outL, outR, input;
+
+ while (numsamples-- > 0) {
+ int i;
+
+ outL = outR = 0;
+ input = (*inputL + *inputR) * gain;
+
+ // Accumulate comb filters in parallel
+ for (i = 0; i < numcombs; i++) {
+ outL += combL[i].process(input);
+ outR += combR[i].process(input);
+ }
+
+ // Feed through allpasses in series
+ for (i = 0; i < numallpasses; i++) {
+ outL = allpassL[i].process(outL);
+ outR = allpassR[i].process(outR);
+ }
+
+ // Calculate output MIXING with anything already there
+ *outputL += outL * wet1 + outR * wet2 + *inputL * dry;
+ *outputR += outR * wet1 + outL * wet2 + *inputR * dry;
+
+ // Increment sample pointers, allowing for interleave (if any)
+ inputL += skip;
+ inputR += skip;
+ outputL += skip;
+ outputR += skip;
+ }
+}
+
+void revmodel::update() {
+ // Recalculate internal values after parameter change
+
+ int i;
+
+ wet1 = wet * (width / 2 + 0.5f);
+ wet2 = wet * ((1 - width) / 2);
+
+ if (mode >= freezemode) {
+ roomsize1 = 1;
+ damp1 = 0;
+ gain = muted;
+ } else {
+ roomsize1 = roomsize;
+ damp1 = damp;
+ gain = fixedgain;
+ }
+
+ for (i = 0; i < numcombs; i++) {
+ combL[i].setfeedback(roomsize1);
+ combR[i].setfeedback(roomsize1);
+ }
+
+ for (i = 0; i < numcombs; i++) {
+ combL[i].setdamp(damp1);
+ combR[i].setdamp(damp1);
+ }
+}
+
+// The following get/set functions are not inlined, because
+// speed is never an issue when calling them, and also
+// because as you develop the reverb model, you may
+// wish to take dynamic action when they are called.
+
+void revmodel::setroomsize(float value) {
+ roomsize = (value * scaleroom) + offsetroom;
+ update();
+}
+
+float revmodel::getroomsize() {
+ return (roomsize - offsetroom) / scaleroom;
+}
+
+void revmodel::setdamp(float value) {
+ damp = value * scaledamp;
+ update();
+}
+
+float revmodel::getdamp() {
+ return damp / scaledamp;
+}
+
+void revmodel::setwet(float value) {
+ wet = value * scalewet;
+ update();
+}
+
+float revmodel::getwet() {
+ return wet / scalewet;
+}
+
+void revmodel::setdry(float value) {
+ dry = value * scaledry;
+}
+
+float revmodel::getdry() {
+ return dry / scaledry;
+}
+
+void revmodel::setwidth(float value) {
+ width = value;
+ update();
+}
+
+float revmodel::getwidth() {
+ return width;
+}
+
+void revmodel::setmode(float value) {
+ mode = value;
+ update();
+}
+
+float revmodel::getmode() {
+ if (mode >= freezemode)
+ return 1;
+ else
+ return 0;
+}
diff --git a/engines/sci/sfx/softseq/mt32/freeverb.h b/engines/sci/sfx/softseq/mt32/freeverb.h
new file mode 100644
index 0000000000..53c5307c5a
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/freeverb.h
@@ -0,0 +1,239 @@
+/* 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$
+ *
+ */
+
+// Macro for killing denormalled numbers
+//
+// Written by Jezar at Dreampoint, June 2000
+// http://www.dreampoint.co.uk
+// Based on IS_DENORMAL macro by Jon Watte
+// This code is public domain
+
+#ifndef FREEVERB_H
+#define FREEVERB_H
+
+#define undenormalise(sample) if (((*(unsigned int*)&sample) & 0x7f800000) == 0) sample = 0.0f
+
+// Comb filter class declaration
+
+class comb {
+public:
+ comb();
+ void setbuffer(float *buf, int size);
+ inline float process(float inp);
+ void mute();
+ void setdamp(float val);
+ float getdamp();
+ void setfeedback(float val);
+ float getfeedback();
+private:
+ float feedback;
+ float filterstore;
+ float damp1;
+ float damp2;
+ float *buffer;
+ int bufsize;
+ int bufidx;
+};
+
+
+// Big to inline - but crucial for speed
+
+inline float comb::process(float input) {
+ float output;
+
+ output = buffer[bufidx];
+ undenormalise(output);
+
+ filterstore = (output * damp2) + (filterstore * damp1);
+ undenormalise(filterstore);
+
+ buffer[bufidx] = input + (filterstore * feedback);
+
+ if (++bufidx >= bufsize)
+ bufidx = 0;
+
+ return output;
+}
+
+// Allpass filter declaration
+
+class allpass {
+public:
+ allpass();
+ void setbuffer(float *buf, int size);
+ inline float process(float inp);
+ void mute();
+ void setfeedback(float val);
+ float getfeedback();
+private:
+ float feedback;
+ float *buffer;
+ int bufsize;
+ int bufidx;
+};
+
+
+// Big to inline - but crucial for speed
+
+inline float allpass::process(float input) {
+ float output;
+ float bufout;
+
+ bufout = buffer[bufidx];
+ undenormalise(bufout);
+
+ output = -input + bufout;
+ buffer[bufidx] = input + (bufout * feedback);
+
+ if (++bufidx >= bufsize)
+ bufidx = 0;
+
+ return output;
+}
+
+
+// Reverb model tuning values
+
+const int numcombs = 8;
+const int numallpasses = 4;
+const float muted = 0;
+const float fixedgain = 0.015f;
+const float scalewet = 3;
+const float scaledry = 2;
+const float scaledamp = 0.4f;
+const float scaleroom = 0.28f;
+const float offsetroom = 0.7f;
+const float initialroom = 0.5f;
+const float initialdamp = 0.5f;
+const float initialwet = 1 / scalewet;
+const float initialdry = 0;
+const float initialwidth = 1;
+const float initialmode = 0;
+const float freezemode = 0.5f;
+const int stereospread = 23;
+
+// These values assume 44.1KHz sample rate
+// they will probably be OK for 48KHz sample rate
+// but would need scaling for 96KHz (or other) sample rates.
+// The values were obtained by listening tests.
+const int combtuningL1 = 1116;
+const int combtuningR1 = 1116 + stereospread;
+const int combtuningL2 = 1188;
+const int combtuningR2 = 1188 + stereospread;
+const int combtuningL3 = 1277;
+const int combtuningR3 = 1277 + stereospread;
+const int combtuningL4 = 1356;
+const int combtuningR4 = 1356 + stereospread;
+const int combtuningL5 = 1422;
+const int combtuningR5 = 1422 + stereospread;
+const int combtuningL6 = 1491;
+const int combtuningR6 = 1491 + stereospread;
+const int combtuningL7 = 1557;
+const int combtuningR7 = 1557 + stereospread;
+const int combtuningL8 = 1617;
+const int combtuningR8 = 1617 + stereospread;
+const int allpasstuningL1 = 556;
+const int allpasstuningR1 = 556 + stereospread;
+const int allpasstuningL2 = 441;
+const int allpasstuningR2 = 441 + stereospread;
+const int allpasstuningL3 = 341;
+const int allpasstuningR3 = 341 + stereospread;
+const int allpasstuningL4 = 225;
+const int allpasstuningR4 = 225 + stereospread;
+
+
+// Reverb model declaration
+
+class revmodel {
+public:
+ revmodel();
+ void mute();
+ void processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip);
+ void processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip);
+ void setroomsize(float value);
+ float getroomsize();
+ void setdamp(float value);
+ float getdamp();
+ void setwet(float value);
+ float getwet();
+ void setdry(float value);
+ float getdry();
+ void setwidth(float value);
+ float getwidth();
+ void setmode(float value);
+ float getmode();
+private:
+ void update();
+
+ float gain;
+ float roomsize, roomsize1;
+ float damp, damp1;
+ float wet, wet1, wet2;
+ float dry;
+ float width;
+ float mode;
+
+ // The following are all declared inline
+ // to remove the need for dynamic allocation
+ // with its subsequent error-checking messiness
+
+ // Comb filters
+ comb combL[numcombs];
+ comb combR[numcombs];
+
+ // Allpass filters
+ allpass allpassL[numallpasses];
+ allpass allpassR[numallpasses];
+
+ // Buffers for the combs
+ float bufcombL1[combtuningL1];
+ float bufcombR1[combtuningR1];
+ float bufcombL2[combtuningL2];
+ float bufcombR2[combtuningR2];
+ float bufcombL3[combtuningL3];
+ float bufcombR3[combtuningR3];
+ float bufcombL4[combtuningL4];
+ float bufcombR4[combtuningR4];
+ float bufcombL5[combtuningL5];
+ float bufcombR5[combtuningR5];
+ float bufcombL6[combtuningL6];
+ float bufcombR6[combtuningR6];
+ float bufcombL7[combtuningL7];
+ float bufcombR7[combtuningR7];
+ float bufcombL8[combtuningL8];
+ float bufcombR8[combtuningR8];
+
+ // Buffers for the allpasses
+ float bufallpassL1[allpasstuningL1];
+ float bufallpassR1[allpasstuningR1];
+ float bufallpassL2[allpasstuningL2];
+ float bufallpassR2[allpasstuningR2];
+ float bufallpassL3[allpasstuningL3];
+ float bufallpassR3[allpasstuningR3];
+ float bufallpassL4[allpasstuningL4];
+ float bufallpassR4[allpasstuningR4];
+};
+
+#endif
diff --git a/engines/sci/sfx/softseq/mt32/i386.cpp b/engines/sci/sfx/softseq/mt32/i386.cpp
new file mode 100644
index 0000000000..f092189d76
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/i386.cpp
@@ -0,0 +1,849 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "mt32emu.h"
+
+#ifdef MT32EMU_HAVE_X86
+
+namespace MT32Emu {
+
+#ifndef _MSC_VER
+
+#define eflag(value) __asm__ __volatile__("pushfl \n popfl \n" : : "a"(value))
+#define cpuid_flag (1 << 21)
+
+static inline bool atti386_DetectCPUID() {
+ unsigned int result;
+
+ // Is there a cpuid?
+ result = cpuid_flag; // set test
+ eflag(result);
+ if (!(result & cpuid_flag))
+ return false;
+
+ result = 0; // clear test
+ eflag(result);
+ if (result & cpuid_flag)
+ return false;
+
+ return true;
+}
+
+static inline bool atti386_DetectSIMD() {
+ unsigned int result;
+
+ if (atti386_DetectCPUID() == false)
+ return false;
+
+ /* check cpuid */
+ __asm__ __volatile__(
+ "pushl %%ebx \n" \
+ "movl $1, %%eax \n" \
+ "cpuid \n" \
+ "movl %%edx, %0 \n" \
+ "popl %%ebx \n" \
+ : "=r"(result) : : "eax", "ecx", "edx");
+
+ if (result & (1 << 25))
+ return true;
+
+ return false;
+}
+
+static inline bool atti386_Detect3DNow() {
+ unsigned int result;
+
+ if (atti386_DetectCPUID() == false)
+ return false;
+
+ // get cpuid
+ __asm__ __volatile__(
+ "pushl %%ebx \n" \
+ "movl $0x80000001, %%eax \n" \
+ "cpuid \n" \
+ "movl %%edx, %0 \n" \
+ "popl %%ebx \n" \
+ : "=r"(result) : : "eax", "ecx", "edx");
+
+ if (result & 0x80000000)
+ return true;
+
+ return false;
+}
+
+
+static inline float atti386_iir_filter_sse(float *output, float *hist1_ptr, float *coef_ptr) {
+ __asm__ __volatile__ (
+ "pushl %1 \n" \
+ "pushl %2 \n" \
+ "movss 0(%0), %%xmm1 \n" \
+ "movups 0(%1), %%xmm2 \n" \
+ "movlps 0(%2), %%xmm3 \n" \
+ " \n" \
+ "shufps $0x44, %%xmm3, %%xmm3 \n" \
+ " \n" \
+ "mulps %%xmm3, %%xmm2 \n" \
+ " \n" \
+ "subss %%xmm2, %%xmm1 \n" \
+ "shufps $0x39, %%xmm2, %%xmm2 \n" \
+ "subss %%xmm2, %%xmm1 \n" \
+ " \n" \
+ "movss %%xmm1, 0(%2) \n" \
+ " \n" \
+ "shufps $0x39, %%xmm2, %%xmm2 \n" \
+ "addss %%xmm2, %%xmm1 \n" \
+ " \n" \
+ "shufps $0x39, %%xmm2, %%xmm2 \n" \
+ "addss %%xmm2, %%xmm1 \n" \
+ " \n" \
+ "movss %%xmm3, 4(%2) \n" \
+ " \n" \
+ "addl $16, %1 \n" \
+ "addl $8, %2 \n" \
+ " \n" \
+ "movups 0(%1), %%xmm2 \n" \
+ " \n" \
+ "movlps 0(%2), %%xmm3 \n" \
+ "shufps $0x44, %%xmm3, %%xmm3 \n" \
+ " \n" \
+ "mulps %%xmm3, %%xmm2 \n" \
+ " \n" \
+ "subss %%xmm2, %%xmm1 \n" \
+ "shufps $0x39, %%xmm2, %%xmm2 \n" \
+ "subss %%xmm2, %%xmm1 \n" \
+ " \n" \
+ "movss %%xmm1, 0(%2) \n" \
+ " \n" \
+ "shufps $0x39, %%xmm2, %%xmm2 \n" \
+ "addss %%xmm2, %%xmm1 \n" \
+ " \n" \
+ "shufps $0x39, %%xmm2, %%xmm2 \n" \
+ "addss %%xmm2, %%xmm1 \n" \
+ " \n" \
+ "movss %%xmm3, 4(%2) \n" \
+ "movss %%xmm1, 0(%0) \n" \
+ "popl %2 \n" \
+ "popl %1 \n" \
+ : : "r"(output), "r"(coef_ptr), "r"(hist1_ptr)
+ : "memory"
+#ifdef __SSE__
+ , "xmm1", "xmm2", "xmm3"
+#endif
+ );
+
+ return *output;
+}
+
+static inline float atti386_iir_filter_3DNow(float output, float *hist1_ptr, float *coef_ptr) {
+ float tmp;
+
+ __asm__ __volatile__ (
+ "movq %0, %%mm1 \n" \
+ " \n" \
+ "movl %1, %%edi \n" \
+ "movq 0(%%edi), %%mm2 \n" \
+ " \n" \
+ "movl %2, %%eax; \n" \
+ "movq 0(%%eax), %%mm3 \n" \
+ " \n" \
+ "pfmul %%mm3, %%mm2 \n" \
+ "pfsub %%mm2, %%mm1 \n" \
+ " \n" \
+ "psrlq $32, %%mm2 \n" \
+ "pfsub %%mm2, %%mm1 \n" \
+ " \n" \
+ "movd %%mm1, %3 \n" \
+ " \n" \
+ "addl $8, %%edi \n" \
+ "movq 0(%%edi), %%mm2 \n" \
+ "movq 0(%%eax), %%mm3 \n" \
+ " \n" \
+ "pfmul %%mm3, %%mm2 \n" \
+ "pfadd %%mm2, %%mm1 \n" \
+ " \n" \
+ "psrlq $32, %%mm2 \n" \
+ "pfadd %%mm2, %%mm1 \n" \
+ " \n" \
+ "pushl %3 \n" \
+ "popl 0(%%eax) \n" \
+ " \n" \
+ "movd %%mm3, 4(%%eax) \n" \
+ " \n" \
+ "addl $8, %%edi \n" \
+ "addl $8, %%eax \n" \
+ " \n" \
+ "movq 0(%%edi), %%mm2 \n" \
+ "movq 0(%%eax), %%mm3 \n" \
+ " \n" \
+ "pfmul %%mm3, %%mm2 \n" \
+ "pfsub %%mm2, %%mm1 \n" \
+ " \n" \
+ "psrlq $32, %%mm2 \n" \
+ "pfsub %%mm2, %%mm1 \n" \
+ " \n" \
+ "movd %%mm1, %3 \n" \
+ " \n" \
+ "addl $8, %%edi \n" \
+ "movq 0(%%edi), %%mm2 \n" \
+ "movq 0(%%eax), %%mm3 \n" \
+ " \n" \
+ "pfmul %%mm3, %%mm2 \n" \
+ "pfadd %%mm2, %%mm1 \n" \
+ " \n" \
+ "psrlq $32, %%mm2 \n" \
+ "pfadd %%mm2, %%mm1 \n" \
+ " \n" \
+ "pushl %3 \n" \
+ "popl 0(%%eax) \n" \
+ "movd %%mm3, 4(%%eax) \n" \
+ " \n" \
+ "movd %%mm1, %0 \n" \
+ "femms \n" \
+ : "=m"(output) : "g"(coef_ptr), "g"(hist1_ptr), "m"(tmp)
+ : "eax", "edi", "memory"
+#ifdef __MMX__
+ , "mm1", "mm2", "mm3"
+#endif
+ );
+
+ return output;
+}
+
+static inline void atti386_produceOutput1(int tmplen, Bit16s myvolume, Bit16s *useBuf, Bit16s *snd) {
+ __asm__ __volatile__(
+ "movl %0, %%ecx \n" \
+ "movw %1, %%ax \n" \
+ "shll $16, %%eax \n" \
+ "movw %1, %%ax \n" \
+ "movd %%eax, %%mm3 \n" \
+ "movd %%eax, %%mm2 \n" \
+ "psllq $32, %%mm3 \n" \
+ "por %%mm2, %%mm3 \n" \
+ "movl %2, %%esi \n" \
+ "movl %3, %%edi \n" \
+ "1: \n" \
+ "movq 0(%%esi), %%mm1 \n" \
+ "movq 0(%%edi), %%mm2 \n" \
+ "pmulhw %%mm3, %%mm1 \n" \
+ "paddw %%mm2, %%mm1 \n" \
+ "movq %%mm1, 0(%%edi) \n" \
+ " \n" \
+ "addl $8, %%esi \n" \
+ "addl $8, %%edi \n" \
+ " \n" \
+ "decl %%ecx \n" \
+ "cmpl $0, %%ecx \n" \
+ "jg 1b \n" \
+ "emms \n" \
+ : : "g"(tmplen), "g"(myvolume), "g"(useBuf), "g"(snd)
+ : "eax", "ecx", "edi", "esi", "memory"
+#ifdef __MMX__
+ , "mm1", "mm2", "mm3"
+#endif
+ );
+}
+
+static inline void atti386_produceOutput2(Bit32u len, Bit16s *snd, float *sndbufl, float *sndbufr, float *multFactor) {
+ __asm__ __volatile__(
+ "movl %4, %%ecx \n" \
+ "shrl $1, %%ecx \n" \
+ "addl $4, %%ecx \n" \
+ "pushl %%ecx \n" \
+ " \n" \
+ "movl %0, %%esi \n" \
+ "movups 0(%%esi), %%xmm1 \n" \
+ " \n" \
+ "movl %1, %%esi \n" \
+ "movl %2, %%edi \n" \
+ "1: \n" \
+ "xorl %%eax, %%eax \n" \
+ "movw 0(%1), %%ax \n" \
+ "cwde \n" \
+ "incl %1 \n" \
+ "incl %1 \n" \
+ "movd %%eax, %%mm1 \n" \
+ "psrlq $32, %%mm1 \n" \
+ "movw 0(%1), %%ax \n" \
+ "incl %1 \n" \
+ "incl %1 \n" \
+ "movd %%eax, %%mm2 \n" \
+ "por %%mm2, %%mm1 \n" \
+ " \n" \
+ "decl %%ecx \n" \
+ "jnz 1b \n" \
+ " \n" \
+ "popl %%ecx \n" \
+ "movl %1, %%esi \n" \
+ "movl %3, %%edi \n" \
+ "incl %%esi \n" \
+ "2: \n" \
+ "decl %%ecx \n" \
+ "jnz 2b \n" \
+ : : "g"(multFactor), "r"(snd), "g"(sndbufl), "g"(sndbufr), "g"(len)
+ : "eax", "ecx", "edi", "esi", "mm1", "mm2", "xmm1", "memory");
+}
+
+static inline void atti386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) {
+ __asm__ __volatile__(
+ "movl %0, %%ecx \n" \
+ "movl %1, %%esi \n" \
+ "movl %2, %%edi \n" \
+ "1: \n" \
+ "movq 0(%%edi), %%mm1 \n" \
+ "movq 0(%%esi), %%mm2 \n" \
+ "paddw %%mm2, %%mm1 \n" \
+ "movq %%mm1, 0(%%esi) \n" \
+ "addl $8, %%edi \n" \
+ "addl $8, %%esi \n" \
+ "decl %%ecx \n" \
+ "cmpl $0, %%ecx \n" \
+ "jg 1b \n" \
+ "emms \n" \
+ : : "g"(len), "g"(buf1), "g"(buf2)
+ : "ecx", "edi", "esi", "memory"
+#ifdef __MMX__
+ , "mm1", "mm2"
+#endif
+ );
+}
+
+static inline void atti386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) {
+ __asm__ __volatile__(
+ "movl %0, %%ecx \n" \
+ "movl %1, %%esi \n" \
+ "movl %2, %%edi \n" \
+ "1: \n" \
+ "movq 0(%%esi), %%mm1 \n" \
+ "movq 0(%%edi), %%mm2 \n" \
+ "movq %%mm1, %%mm3 \n" \
+ "pmulhw %%mm2, %%mm1 \n" \
+ "paddw %%mm3, %%mm1 \n" \
+ "movq %%mm1, 0(%%esi) \n" \
+ "addl $8, %%edi \n" \
+ "addl $8, %%esi \n" \
+ "decl %%ecx \n" \
+ "cmpl $0, %%ecx \n" \
+ "jg 1b \n" \
+ "emms \n" \
+ : : "g"(len), "g"(buf1), "g"(buf2)
+ : "ecx", "edi", "esi", "memory"
+#ifdef __MMX__
+ , "mm1", "mm2", "mm3"
+#endif
+ );
+}
+
+static inline void atti386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) {
+ __asm__ __volatile__(
+ "movl %0, %%ecx \n" \
+ "movl %1, %%esi \n" \
+ "movl %2, %%edi \n" \
+ "1: \n" \
+ "movq 0(%%esi), %%mm1 \n" \
+ "movq 0(%%edi), %%mm2 \n" \
+ "pmulhw %%mm2, %%mm1 \n" \
+ "movq %%mm1, 0(%%esi) \n" \
+ "addl $8, %%edi \n" \
+ "addl $8, %%esi \n" \
+ "decl %%ecx \n" \
+ "cmpl $0, %%ecx \n" \
+ "jg 1b \n" \
+ "emms \n" \
+ : : "g"(len), "g"(buf1), "g"(buf2)
+ : "ecx", "edi", "esi", "memory"
+#ifdef __MMX__
+ , "mm1", "mm2"
+#endif
+ );
+}
+
+static inline void atti386_partialProductOutput(int quadlen, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *p1buf) {
+ __asm__ __volatile__(
+ "movl %0, %%ecx \n" \
+ "movw %1, %%ax \n" \
+ "shll $16, %%eax \n" \
+ "movw %2, %%ax \n" \
+ "movd %%eax, %%mm1 \n" \
+ "movd %%eax, %%mm2 \n" \
+ "psllq $32, %%mm1 \n" \
+ "por %%mm2, %%mm1 \n" \
+ "movl %3, %%edi \n" \
+ "movl %4, %%esi \n" \
+ "pushl %%ebx \n" \
+ "1: \n" \
+ "movw 0(%%esi), %%bx \n" \
+ "addl $2, %%esi \n" \
+ "movw 0(%%esi), %%dx \n" \
+ "addl $2, %%esi \n" \
+ "" \
+ "movw %%dx, %%ax \n" \
+ "shll $16, %%eax \n" \
+ "movw %%dx, %%ax \n" \
+ "movd %%eax, %%mm2 \n" \
+ "psllq $32, %%mm2 \n" \
+ "movw %%bx, %%ax \n" \
+ "shll $16, %%eax \n" \
+ "movw %%bx, %%ax \n" \
+ "movd %%eax, %%mm3 \n" \
+ "por %%mm3, %%mm2 \n" \
+ "" \
+ "pmulhw %%mm1, %%mm2 \n" \
+ "movq %%mm2, 0(%%edi) \n" \
+ "addl $8, %%edi \n" \
+ "" \
+ "decl %%ecx \n" \
+ "cmpl $0, %%ecx \n" \
+ "jg 1b \n" \
+ "emms \n" \
+ "popl %%ebx \n" \
+ : : "g"(quadlen), "g"(leftvol), "g"(rightvol), "g"(partialBuf), "g"(p1buf)
+ : "eax", "ecx", "edx", "edi", "esi", "memory"
+#ifdef __MMX__
+ , "mm1", "mm2", "mm3"
+#endif
+ );
+}
+
+#endif
+
+bool DetectSIMD() {
+#ifdef _MSC_VER
+ bool found_simd;
+ __asm {
+ pushfd
+ pop eax // get EFLAGS into eax
+ mov ebx,eax // keep a copy
+ xor eax,0x200000
+ // toggle CPUID bit
+
+ push eax
+ popfd // set new EFLAGS
+ pushfd
+ pop eax // EFLAGS back into eax
+
+ xor eax,ebx
+ // have we changed the ID bit?
+
+ je NO_SIMD
+ // No, no CPUID instruction
+
+ // we could toggle the
+ // ID bit so CPUID is present
+ mov eax,1
+
+ cpuid // get processor features
+ test edx,1<<25 // check the SIMD bit
+ jz NO_SIMD
+ mov found_simd,1
+ jmp DONE
+ NO_SIMD:
+ mov found_simd,0
+ DONE:
+ }
+ return found_simd;
+#else
+ return atti386_DetectSIMD();
+#endif
+}
+
+bool Detect3DNow() {
+#ifdef _MSC_VER
+ bool found3D = false;
+ __asm {
+ pushfd
+ pop eax
+ mov edx, eax
+ xor eax, 00200000h
+ push eax
+ popfd
+ pushfd
+ pop eax
+ xor eax, edx
+ jz NO_3DNOW
+
+ mov eax, 80000000h
+ cpuid
+
+ cmp eax, 80000000h
+ jbe NO_3DNOW
+
+ mov eax, 80000001h
+ cpuid
+ test edx, 80000000h
+ jz NO_3DNOW
+ mov found3D, 1
+NO_3DNOW:
+
+ }
+ return found3D;
+#else
+ return atti386_Detect3DNow();
+#endif
+}
+
+float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr) {
+ float output;
+
+ // 1st number of coefficients array is overall input scale factor, or filter gain
+ output = input * (*coef_ptr++);
+
+#ifdef _MSC_VER
+ __asm {
+
+ movss xmm1, output
+
+ mov eax, coef_ptr
+ movups xmm2, [eax]
+
+ mov eax, hist1_ptr
+ movlps xmm3, [eax]
+ shufps xmm3, xmm3, 44h
+ // hist1_ptr+1, hist1_ptr, hist1_ptr+1, hist1_ptr
+
+ mulps xmm2, xmm3
+
+ subss xmm1, xmm2
+ // Rotate elements right
+ shufps xmm2, xmm2, 39h
+ subss xmm1, xmm2
+
+ // Store new_hist
+ movss DWORD PTR [eax], xmm1
+
+ // Rotate elements right
+ shufps xmm2, xmm2, 39h
+ addss xmm1, xmm2
+
+ // Rotate elements right
+ shufps xmm2, xmm2, 39h
+ addss xmm1, xmm2
+
+ // Store previous hist
+ movss DWORD PTR [eax+4], xmm3
+
+ add coef_ptr, 16
+ add hist1_ptr, 8
+
+ mov eax, coef_ptr
+ movups xmm2, [eax]
+
+ mov eax, hist1_ptr
+ movlps xmm3, [eax]
+ shufps xmm3, xmm3, 44h
+ // hist1_ptr+1, hist1_ptr, hist1_ptr+1, hist1_ptr
+
+ mulps xmm2, xmm3
+
+ subss xmm1, xmm2
+ // Rotate elements right
+ shufps xmm2, xmm2, 39h
+ subss xmm1, xmm2
+
+ // Store new_hist
+ movss DWORD PTR [eax], xmm1
+
+ // Rotate elements right
+ shufps xmm2, xmm2, 39h
+ addss xmm1, xmm2
+
+ // Rotate elements right
+ shufps xmm2, xmm2, 39h
+ addss xmm1, xmm2
+
+ // Store previous hist
+ movss DWORD PTR [eax+4], xmm3
+
+ movss output, xmm1
+ }
+#else
+ output = atti386_iir_filter_sse(&output, hist1_ptr, coef_ptr);
+#endif
+ return output;
+}
+
+float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr) {
+ float output;
+
+ // 1st number of coefficients array is overall input scale factor, or filter gain
+ output = input * (*coef_ptr++);
+
+ // I find it very sad that 3DNow requires twice as many instructions as Intel's SSE
+ // Intel does have the upper hand here.
+#ifdef _MSC_VER
+ float tmp;
+ __asm {
+ movq mm1, output
+ mov ebx, coef_ptr
+ movq mm2, [ebx]
+
+ mov eax, hist1_ptr;
+ movq mm3, [eax]
+
+ pfmul mm2, mm3
+ pfsub mm1, mm2
+
+ psrlq mm2, 32
+ pfsub mm1, mm2
+
+ // Store new hist
+ movd tmp, mm1
+
+ add ebx, 8
+ movq mm2, [ebx]
+ movq mm3, [eax]
+
+ pfmul mm2, mm3
+ pfadd mm1, mm2
+
+ psrlq mm2, 32
+ pfadd mm1, mm2
+
+ push tmp
+ pop DWORD PTR [eax]
+
+ movd DWORD PTR [eax+4], mm3
+
+ add ebx, 8
+ add eax, 8
+
+ movq mm2, [ebx]
+ movq mm3, [eax]
+
+ pfmul mm2, mm3
+ pfsub mm1, mm2
+
+ psrlq mm2, 32
+ pfsub mm1, mm2
+
+ // Store new hist
+ movd tmp, mm1
+
+ add ebx, 8
+ movq mm2, [ebx]
+ movq mm3, [eax]
+
+ pfmul mm2, mm3
+ pfadd mm1, mm2
+
+ psrlq mm2, 32
+ pfadd mm1, mm2
+
+ push tmp
+ pop DWORD PTR [eax]
+ movd DWORD PTR [eax+4], mm3
+
+ movd output, mm1
+
+ femms
+ }
+#else
+ output = atti386_iir_filter_3DNow(output, hist1_ptr, coef_ptr);
+#endif
+ return output;
+}
+
+#if MT32EMU_USE_MMX > 0
+
+int i386_partialProductOutput(int len, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *mixedBuf) {
+ int tmplen = len >> 1;
+ if (tmplen == 0) {
+ return 0;
+ }
+#ifdef _MSC_VER
+ __asm {
+ mov ecx,tmplen
+ mov ax, leftvol
+ shl eax,16
+ mov ax, rightvol
+ movd mm1, eax
+ movd mm2, eax
+ psllq mm1, 32
+ por mm1, mm2
+ mov edi, partialBuf
+ mov esi, mixedBuf
+mmxloop1:
+ mov bx, [esi]
+ add esi,2
+ mov dx, [esi]
+ add esi,2
+
+ mov ax, dx
+ shl eax, 16
+ mov ax, dx
+ movd mm2,eax
+ psllq mm2, 32
+ mov ax, bx
+ shl eax, 16
+ mov ax, bx
+ movd mm3,eax
+ por mm2,mm3
+
+ pmulhw mm2, mm1
+ movq [edi], mm2
+ add edi, 8
+
+ dec ecx
+ cmp ecx,0
+ jg mmxloop1
+ emms
+ }
+#else
+ atti386_partialProductOutput(tmplen, leftvol, rightvol, partialBuf, mixedBuf);
+#endif
+ return tmplen << 1;
+}
+
+int i386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) {
+ int tmplen = len >> 2;
+ if (tmplen == 0) {
+ return 0;
+ }
+#ifdef _MSC_VER
+ __asm {
+ mov ecx, tmplen
+ mov esi, buf1
+ mov edi, buf2
+
+mixloop1:
+ movq mm1, [edi]
+ movq mm2, [esi]
+ paddw mm1,mm2
+ movq [esi],mm1
+ add edi,8
+ add esi,8
+
+ dec ecx
+ cmp ecx,0
+ jg mixloop1
+ emms
+ }
+#else
+ atti386_mixBuffers(buf1, buf2, tmplen);
+#endif
+ return tmplen << 2;
+}
+
+
+int i386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) {
+ int tmplen = len >> 2;
+ if (tmplen == 0) {
+ return 0;
+ }
+#ifdef _MSC_VER
+ __asm {
+ mov ecx, tmplen
+ mov esi, buf1
+ mov edi, buf2
+
+mixloop2:
+ movq mm1, [esi]
+ movq mm2, [edi]
+ movq mm3, mm1
+ pmulhw mm1, mm2
+ paddw mm1,mm3
+ movq [esi],mm1
+ add edi,8
+ add esi,8
+
+ dec ecx
+ cmp ecx,0
+ jg mixloop2
+ emms
+ }
+#else
+ atti386_mixBuffersRingMix(buf1, buf2, tmplen);
+#endif
+ return tmplen << 2;
+}
+
+int i386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) {
+ int tmplen = len >> 2;
+ if (tmplen == 0) {
+ return 0;
+ }
+#ifdef _MSC_VER
+ __asm {
+ mov ecx, tmplen
+ mov esi, buf1
+ mov edi, buf2
+
+mixloop3:
+ movq mm1, [esi]
+ movq mm2, [edi]
+ pmulhw mm1, mm2
+ movq [esi],mm1
+ add edi,8
+ add esi,8
+
+ dec ecx
+ cmp ecx,0
+ jg mixloop3
+ emms
+ }
+#else
+ atti386_mixBuffersRing(buf1, buf2, tmplen);
+#endif
+ return tmplen << 2;
+}
+
+int i386_produceOutput1(Bit16s *useBuf, Bit16s *stream, Bit32u len, Bit16s volume) {
+ int tmplen = (len >> 1);
+ if (tmplen == 0) {
+ return 0;
+ }
+#ifdef _MSC_VER
+ __asm {
+ mov ecx, tmplen
+ mov ax,volume
+ shl eax,16
+ mov ax,volume
+ movd mm3,eax
+ movd mm2,eax
+ psllq mm3, 32
+ por mm3,mm2
+ mov esi, useBuf
+ mov edi, stream
+mixloop4:
+ movq mm1, [esi]
+ movq mm2, [edi]
+ pmulhw mm1, mm3
+ paddw mm1,mm2
+ movq [edi], mm1
+
+ add esi,8
+ add edi,8
+
+ dec ecx
+ cmp ecx,0
+ jg mixloop4
+ emms
+ }
+#else
+ atti386_produceOutput1(tmplen, volume, useBuf, stream);
+#endif
+ return tmplen << 1;
+}
+
+#endif
+
+}
+
+#endif
diff --git a/engines/sci/sfx/softseq/mt32/i386.h b/engines/sci/sfx/softseq/mt32/i386.h
new file mode 100644
index 0000000000..e8644411cd
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/i386.h
@@ -0,0 +1,49 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef MT32EMU_I386_H
+#define MT32EMU_I386_H
+
+namespace MT32Emu {
+#ifdef MT32EMU_HAVE_X86
+
+// Function that detects the availablity of SSE SIMD instructions
+bool DetectSIMD();
+// Function that detects the availablity of 3DNow instructions
+bool Detect3DNow();
+
+float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr);
+float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr);
+float iir_filter_normal(float input,float *hist1_ptr, float *coef_ptr);
+
+#if MT32EMU_USE_MMX > 0
+int i386_partialProductOutput(int len, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *mixedBuf);
+int i386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len);
+int i386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len);
+int i386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len);
+int i386_produceOutput1(Bit16s *useBuf, Bit16s *stream, Bit32u len, Bit16s volume);
+#endif
+
+#endif
+
+}
+
+#endif
diff --git a/engines/sci/sfx/softseq/mt32/mt32_file.cpp b/engines/sci/sfx/softseq/mt32/mt32_file.cpp
new file mode 100644
index 0000000000..86cb29fd49
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/mt32_file.cpp
@@ -0,0 +1,112 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+
+#include "mt32emu.h"
+
+namespace MT32Emu {
+
+ bool ANSIFile::open(const char *filename, OpenMode mode) {
+ const char *fmode;
+ if (mode == OpenMode_read) {
+ fmode = "rb";
+ } else {
+ fmode = "wb";
+ }
+ fp = fopen(filename, fmode);
+ return (fp != NULL);
+ }
+
+ void ANSIFile::close() {
+ fclose(fp);
+ }
+
+ size_t ANSIFile::read(void *in, size_t size) {
+ return fread(in, 1, size, fp);
+ }
+
+ bool ANSIFile::readLine(char *in, size_t size) {
+ return fgets(in, (int)size, fp) != NULL;
+ }
+
+ bool ANSIFile::readBit8u(Bit8u *in) {
+ int c = fgetc(fp);
+ if (c == EOF)
+ return false;
+ *in = (Bit8u)c;
+ return true;
+ }
+
+ bool File::readBit16u(Bit16u *in) {
+ Bit8u b[2];
+ if (read(&b[0], 2) != 2)
+ return false;
+ *in = ((b[0] << 8) | b[1]);
+ return true;
+ }
+
+ bool File::readBit32u(Bit32u *in) {
+ Bit8u b[4];
+ if (read(&b[0], 4) != 4)
+ return false;
+ *in = ((b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]);
+ return true;
+ }
+
+ size_t ANSIFile::write(const void *out, size_t size) {
+ return fwrite(out, 1, size, fp);
+ }
+
+ bool ANSIFile::writeBit8u(Bit8u out) {
+ return fputc(out, fp) != EOF;
+ }
+
+ bool File::writeBit16u(Bit16u out) {
+ if (!writeBit8u((Bit8u)((out & 0xFF00) >> 8))) {
+ return false;
+ }
+ if (!writeBit8u((Bit8u)(out & 0x00FF))) {
+ return false;
+ }
+ return true;
+ }
+
+ bool File::writeBit32u(Bit32u out) {
+ if (!writeBit8u((Bit8u)((out & 0xFF000000) >> 24))) {
+ return false;
+ }
+ if (!writeBit8u((Bit8u)((out & 0x00FF0000) >> 16))) {
+ return false;
+ }
+ if (!writeBit8u((Bit8u)((out & 0x0000FF00) >> 8))) {
+ return false;
+ }
+ if (!writeBit8u((Bit8u)(out & 0x000000FF))) {
+ return false;
+ }
+ return true;
+ }
+
+ bool ANSIFile::isEOF() {
+ return feof(fp) != 0;
+ }
+}
diff --git a/engines/sci/sfx/softseq/mt32/mt32_file.h b/engines/sci/sfx/softseq/mt32/mt32_file.h
new file mode 100644
index 0000000000..5f05c9e9ae
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/mt32_file.h
@@ -0,0 +1,67 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef MT32EMU_FILE_H
+#define MT32EMU_FILE_H
+
+#include <stdio.h>
+
+namespace MT32Emu {
+
+class File {
+public:
+ enum OpenMode {
+ OpenMode_read = 0,
+ OpenMode_write = 1
+ };
+ virtual ~File() {}
+ virtual void close() = 0;
+ virtual size_t read(void *in, size_t size) = 0;
+ virtual bool readLine(char *in, size_t size) = 0;
+ virtual bool readBit8u(Bit8u *in) = 0;
+ virtual bool readBit16u(Bit16u *in);
+ virtual bool readBit32u(Bit32u *in);
+ virtual size_t write(const void *out, size_t size) = 0;
+ virtual bool writeBit8u(Bit8u out) = 0;
+ // Note: May write a single byte to the file before failing
+ virtual bool writeBit16u(Bit16u out);
+ // Note: May write some (<4) bytes to the file before failing
+ virtual bool writeBit32u(Bit32u out);
+ virtual bool isEOF() = 0;
+};
+
+class ANSIFile: public File {
+private:
+ FILE *fp;
+public:
+ bool open(const char *filename, OpenMode mode);
+ void close();
+ size_t read(void *in, size_t size);
+ bool readLine(char *in, size_t size);
+ bool readBit8u(Bit8u *in);
+ size_t write(const void *out, size_t size);
+ bool writeBit8u(Bit8u out);
+ bool isEOF();
+};
+
+}
+
+#endif
diff --git a/engines/sci/sfx/softseq/mt32/mt32emu.h b/engines/sci/sfx/softseq/mt32/mt32emu.h
new file mode 100644
index 0000000000..0aa4df7488
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/mt32emu.h
@@ -0,0 +1,70 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef MT32EMU_MT32EMU_H
+#define MT32EMU_MT32EMU_H
+
+// Debugging
+// Show the instruments played
+#define MT32EMU_MONITOR_INSTRUMENTS 1
+// Shows number of partials MT-32 is playing, and on which parts
+#define MT32EMU_MONITOR_PARTIALS 0
+// Determines how the waveform cache file is handled (must be regenerated after sampling rate change)
+#define MT32EMU_WAVECACHEMODE 0 // Load existing cache if possible, otherwise generate and save cache
+//#define MT32EMU_WAVECACHEMODE 1 // Load existing cache if possible, otherwise generage but don't save cache
+//#define MT32EMU_WAVECACHEMODE 2 // Ignore existing cache, generate and save cache
+//#define MT32EMU_WAVECACHEMODE 3 // Ignore existing cache, generate but don't save cache
+
+// Configuration
+// The maximum number of partials playing simultaneously
+#define MT32EMU_MAX_PARTIALS 32
+// The maximum number of notes playing simultaneously per part.
+// No point making it more than MT32EMU_MAX_PARTIALS, since each note needs at least one partial.
+#define MT32EMU_MAX_POLY 32
+// This calculates the exact frequencies of notes as they are played, instead of offsetting from pre-cached semitones. Potentially very slow.
+#define MT32EMU_ACCURATENOTES 0
+
+#if (defined (_MSC_VER) && defined(_M_IX86))
+#define MT32EMU_HAVE_X86
+#elif defined(__GNUC__)
+#if __GNUC__ >= 3 && defined(__i386__)
+#define MT32EMU_HAVE_X86
+#endif
+#endif
+
+#ifdef MT32EMU_HAVE_X86
+#define MT32EMU_USE_MMX 1
+#else
+#define MT32EMU_USE_MMX 0
+#endif
+
+#include "freeverb.h"
+
+#include "structures.h"
+#include "i386.h"
+#include "mt32_file.h"
+#include "tables.h"
+#include "partial.h"
+#include "partialManager.h"
+#include "part.h"
+#include "synth.h"
+
+#endif
diff --git a/engines/sci/sfx/softseq/mt32/part.cpp b/engines/sci/sfx/softseq/mt32/part.cpp
new file mode 100644
index 0000000000..b3d71bccca
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/part.cpp
@@ -0,0 +1,632 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <string.h>
+#include <math.h>
+
+#include "mt32emu.h"
+
+namespace MT32Emu {
+
+static const Bit8u PartialStruct[13] = {
+ 0, 0, 2, 2, 1, 3,
+ 3, 0, 3, 0, 2, 1, 3 };
+
+static const Bit8u PartialMixStruct[13] = {
+ 0, 1, 0, 1, 1, 0,
+ 1, 3, 3, 2, 2, 2, 2 };
+
+static const float floatKeyfollow[17] = {
+ -1.0f, -1.0f/2.0f, -1.0f/4.0f, 0.0f,
+ 1.0f/8.0f, 1.0f/4.0f, 3.0f/8.0f, 1.0f/2.0f, 5.0f/8.0f, 3.0f/4.0f, 7.0f/8.0f, 1.0f,
+ 5.0f/4.0f, 3.0f/2.0f, 2.0f,
+ 1.0009765625f, 1.0048828125f
+};
+
+//FIXME:KG: Put this dpoly stuff somewhere better
+bool dpoly::isActive() const {
+ return partials[0] != NULL || partials[1] != NULL || partials[2] != NULL || partials[3] != NULL;
+}
+
+Bit32u dpoly::getAge() const {
+ for (int i = 0; i < 4; i++) {
+ if (partials[i] != NULL) {
+ return partials[i]->age;
+ }
+ }
+ return 0;
+}
+
+RhythmPart::RhythmPart(Synth *useSynth, unsigned int usePartNum): Part(useSynth, usePartNum) {
+ strcpy(name, "Rhythm");
+ rhythmTemp = &synth->mt32ram.rhythmSettings[0];
+ refresh();
+}
+
+Part::Part(Synth *useSynth, unsigned int usePartNum) {
+ this->synth = useSynth;
+ this->partNum = usePartNum;
+ patchCache[0].dirty = true;
+ holdpedal = false;
+ patchTemp = &synth->mt32ram.patchSettings[partNum];
+ if (usePartNum == 8) {
+ // Nasty hack for rhythm
+ timbreTemp = NULL;
+ } else {
+ sprintf(name, "Part %d", partNum + 1);
+ timbreTemp = &synth->mt32ram.timbreSettings[partNum];
+ }
+ currentInstr[0] = 0;
+ currentInstr[10] = 0;
+ expression = 127;
+ volumeMult = 0;
+ volumesetting.leftvol = 32767;
+ volumesetting.rightvol = 32767;
+ bend = 0.0f;
+ memset(polyTable,0,sizeof(polyTable));
+ memset(patchCache, 0, sizeof(patchCache));
+}
+
+void Part::setHoldPedal(bool pedalval) {
+ if (holdpedal && !pedalval) {
+ holdpedal = false;
+ stopPedalHold();
+ } else {
+ holdpedal = pedalval;
+ }
+}
+
+void RhythmPart::setBend(unsigned int midiBend) {
+ synth->printDebug("%s: Setting bend (%d) not supported on rhythm", name, midiBend);
+ return;
+}
+
+void Part::setBend(unsigned int midiBend) {
+ // FIXME:KG: Slightly unbalanced increments, but I wanted min -1.0, centre 0.0 and max 1.0
+ if (midiBend <= 0x2000) {
+ bend = ((signed int)midiBend - 0x2000) / (float)0x2000;
+ } else {
+ bend = ((signed int)midiBend - 0x2000) / (float)0x1FFF;
+ }
+ // Loop through all partials to update their bend
+ for (int i = 0; i < MT32EMU_MAX_POLY; i++) {
+ for (int j = 0; j < 4; j++) {
+ if (polyTable[i].partials[j] != NULL) {
+ polyTable[i].partials[j]->setBend(bend);
+ }
+ }
+ }
+}
+
+void RhythmPart::setModulation(unsigned int midiModulation) {
+ synth->printDebug("%s: Setting modulation (%d) not supported on rhythm", name, midiModulation);
+}
+
+void Part::setModulation(unsigned int midiModulation) {
+ // Just a bloody guess, as always, before I get things figured out
+ for (int t = 0; t < 4; t++) {
+ if (patchCache[t].playPartial) {
+ int newrate = (patchCache[t].modsense * midiModulation) >> 7;
+ //patchCache[t].lfoperiod = lfotable[newrate];
+ patchCache[t].lfodepth = newrate;
+ //FIXME:KG: timbreTemp->partial[t].lfo.depth =
+ }
+ }
+}
+
+void RhythmPart::refresh() {
+ updateVolume();
+ // (Re-)cache all the mapped timbres ahead of time
+ for (unsigned int drumNum = 0; drumNum < synth->controlROMMap->rhythmSettingsCount; drumNum++) {
+ int drumTimbreNum = rhythmTemp[drumNum].timbre;
+ if (drumTimbreNum >= 127) // 94 on MT-32
+ continue;
+ Bit16s pan = rhythmTemp[drumNum].panpot; // They use R-L 0-14...
+ // FIXME:KG: Panning cache should be backed up to partials using it, too
+ if (pan < 7) {
+ drumPan[drumNum].leftvol = pan * 4681;
+ drumPan[drumNum].rightvol = 32767;
+ } else {
+ drumPan[drumNum].rightvol = (14 - pan) * 4681;
+ drumPan[drumNum].leftvol = 32767;
+ }
+ PatchCache *cache = drumCache[drumNum];
+ backupCacheToPartials(cache);
+ for (int t = 0; t < 4; t++) {
+ // Common parameters, stored redundantly
+ cache[t].dirty = true;
+ cache[t].pitchShift = 0.0f;
+ cache[t].benderRange = 0.0f;
+ cache[t].pansetptr = &drumPan[drumNum];
+ cache[t].reverb = rhythmTemp[drumNum].reverbSwitch > 0;
+ }
+ }
+}
+
+void Part::refresh() {
+ updateVolume();
+ backupCacheToPartials(patchCache);
+ for (int t = 0; t < 4; t++) {
+ // Common parameters, stored redundantly
+ patchCache[t].dirty = true;
+ patchCache[t].pitchShift = (patchTemp->patch.keyShift - 24) + (patchTemp->patch.fineTune - 50) / 100.0f;
+ patchCache[t].benderRange = patchTemp->patch.benderRange;
+ patchCache[t].pansetptr = &volumesetting;
+ patchCache[t].reverb = patchTemp->patch.reverbSwitch > 0;
+ }
+ memcpy(currentInstr, timbreTemp->common.name, 10);
+}
+
+const char *Part::getCurrentInstr() const {
+ return &currentInstr[0];
+}
+
+void RhythmPart::refreshTimbre(unsigned int absTimbreNum) {
+ for (int m = 0; m < 85; m++) {
+ if (rhythmTemp[m].timbre == absTimbreNum - 128)
+ drumCache[m][0].dirty = true;
+ }
+}
+
+void Part::refreshTimbre(unsigned int absTimbreNum) {
+ if (getAbsTimbreNum() == absTimbreNum) {
+ memcpy(currentInstr, timbreTemp->common.name, 10);
+ patchCache[0].dirty = true;
+ }
+}
+
+int Part::fixBiaslevel(int srcpnt, int *dir) {
+ int noteat = srcpnt & 0x3F;
+ int outnote;
+ if (srcpnt < 64)
+ *dir = 0;
+ else
+ *dir = 1;
+ outnote = 33 + noteat;
+ //synth->printDebug("Bias note %d, dir %d", outnote, *dir);
+
+ return outnote;
+}
+
+int Part::fixKeyfollow(int srckey) {
+ if (srckey>=0 && srckey<=16) {
+ int keyfix[17] = { -256*16, -128*16, -64*16, 0, 32*16, 64*16, 96*16, 128*16, (128+32)*16, 192*16, (192+32)*16, 256*16, (256+64)*16, (256+128)*16, (512)*16, 4100, 4116};
+ return keyfix[srckey];
+ } else {
+ //LOG(LOG_ERROR|LOG_MISC,"Missed key: %d", srckey);
+ return 256;
+ }
+}
+
+void Part::abortPoly(dpoly *poly) {
+ if (!poly->isPlaying) {
+ return;
+ }
+ for (int i = 0; i < 4; i++) {
+ Partial *partial = poly->partials[i];
+ if (partial != NULL) {
+ partial->deactivate();
+ }
+ }
+ poly->isPlaying = false;
+}
+
+void Part::setPatch(const PatchParam *patch) {
+ patchTemp->patch = *patch;
+}
+
+void RhythmPart::setTimbre(TimbreParam * /*timbre*/) {
+ synth->printDebug("%s: Attempted to call setTimbre() - doesn't make sense for rhythm", name);
+}
+
+void Part::setTimbre(TimbreParam *timbre) {
+ *timbreTemp = *timbre;
+}
+
+unsigned int RhythmPart::getAbsTimbreNum() const {
+ synth->printDebug("%s: Attempted to call getAbsTimbreNum() - doesn't make sense for rhythm", name);
+ return 0;
+}
+
+unsigned int Part::getAbsTimbreNum() const {
+ return (patchTemp->patch.timbreGroup * 64) + patchTemp->patch.timbreNum;
+}
+
+void RhythmPart::setProgram(unsigned int patchNum) {
+ synth->printDebug("%s: Attempt to set program (%d) on rhythm is invalid", name, patchNum);
+}
+
+void Part::setProgram(unsigned int patchNum) {
+ setPatch(&synth->mt32ram.patches[patchNum]);
+ setTimbre(&synth->mt32ram.timbres[getAbsTimbreNum()].timbre);
+
+ refresh();
+
+ allSoundOff(); //FIXME:KG: Is this correct?
+}
+
+void Part::backupCacheToPartials(PatchCache cache[4]) {
+ // check if any partials are still playing with the old patch cache
+ // if so then duplicate the cached data from the part to the partial so that
+ // we can change the part's cache without affecting the partial.
+ // We delay this until now to avoid a copy operation with every note played
+ for (int m = 0; m < MT32EMU_MAX_POLY; m++) {
+ for (int i = 0; i < 4; i++) {
+ Partial *partial = polyTable[m].partials[i];
+ if (partial != NULL && partial->patchCache == &cache[i]) {
+ partial->cachebackup = cache[i];
+ partial->patchCache = &partial->cachebackup;
+ }
+ }
+ }
+}
+
+void Part::cacheTimbre(PatchCache cache[4], const TimbreParam *timbre) {
+ backupCacheToPartials(cache);
+ int partialCount = 0;
+ for (int t = 0; t < 4; t++) {
+ if (((timbre->common.pmute >> t) & 0x1) == 1) {
+ cache[t].playPartial = true;
+ partialCount++;
+ } else {
+ cache[t].playPartial = false;
+ continue;
+ }
+
+ // Calculate and cache common parameters
+
+ cache[t].pcm = timbre->partial[t].wg.pcmwave;
+ cache[t].useBender = (timbre->partial[t].wg.bender == 1);
+
+ switch (t) {
+ case 0:
+ cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct12] & 0x2) ? true : false;
+ cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct12];
+ cache[t].structurePosition = 0;
+ cache[t].structurePair = 1;
+ break;
+ case 1:
+ cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct12] & 0x1) ? true : false;
+ cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct12];
+ cache[t].structurePosition = 1;
+ cache[t].structurePair = 0;
+ break;
+ case 2:
+ cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct34] & 0x2) ? true : false;
+ cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct34];
+ cache[t].structurePosition = 0;
+ cache[t].structurePair = 3;
+ break;
+ case 3:
+ cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct34] & 0x1) ? true : false;
+ cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct34];
+ cache[t].structurePosition = 1;
+ cache[t].structurePair = 2;
+ break;
+ default:
+ break;
+ }
+
+ cache[t].waveform = timbre->partial[t].wg.waveform;
+ cache[t].pulsewidth = timbre->partial[t].wg.pulsewid;
+ cache[t].pwsens = timbre->partial[t].wg.pwvelo;
+ if (timbre->partial[t].wg.keyfollow > 16) {
+ synth->printDebug("Bad keyfollow value in timbre!");
+ cache[t].pitchKeyfollow = 1.0f;
+ } else {
+ cache[t].pitchKeyfollow = floatKeyfollow[timbre->partial[t].wg.keyfollow];
+ }
+
+ cache[t].pitch = timbre->partial[t].wg.coarse + (timbre->partial[t].wg.fine - 50) / 100.0f + 24.0f;
+ cache[t].pitchEnv = timbre->partial[t].env;
+ cache[t].pitchEnv.sensitivity = (char)((float)cache[t].pitchEnv.sensitivity * 1.27f);
+ cache[t].pitchsustain = cache[t].pitchEnv.level[3];
+
+ // Calculate and cache TVA envelope stuff
+ cache[t].ampEnv = timbre->partial[t].tva;
+ cache[t].ampEnv.level = (char)((float)cache[t].ampEnv.level * 1.27f);
+
+ cache[t].ampbias[0] = fixBiaslevel(cache[t].ampEnv.biaspoint1, &cache[t].ampdir[0]);
+ cache[t].ampblevel[0] = 12 - cache[t].ampEnv.biaslevel1;
+ cache[t].ampbias[1] = fixBiaslevel(cache[t].ampEnv.biaspoint2, &cache[t].ampdir[1]);
+ cache[t].ampblevel[1] = 12 - cache[t].ampEnv.biaslevel2;
+ cache[t].ampdepth = cache[t].ampEnv.envvkf * cache[t].ampEnv.envvkf;
+
+ // Calculate and cache filter stuff
+ cache[t].filtEnv = timbre->partial[t].tvf;
+ cache[t].filtkeyfollow = fixKeyfollow(cache[t].filtEnv.keyfollow);
+ cache[t].filtEnv.envdepth = (char)((float)cache[t].filtEnv.envdepth * 1.27);
+ cache[t].tvfbias = fixBiaslevel(cache[t].filtEnv.biaspoint, &cache[t].tvfdir);
+ cache[t].tvfblevel = cache[t].filtEnv.biaslevel;
+ cache[t].filtsustain = cache[t].filtEnv.envlevel[3];
+
+ // Calculate and cache LFO stuff
+ cache[t].lfodepth = timbre->partial[t].lfo.depth;
+ cache[t].lfoperiod = synth->tables.lfoPeriod[(int)timbre->partial[t].lfo.rate];
+ cache[t].lforate = timbre->partial[t].lfo.rate;
+ cache[t].modsense = timbre->partial[t].lfo.modsense;
+ }
+ for (int t = 0; t < 4; t++) {
+ // Common parameters, stored redundantly
+ cache[t].dirty = false;
+ cache[t].partialCount = partialCount;
+ cache[t].sustain = (timbre->common.nosustain == 0);
+ }
+ //synth->printDebug("Res 1: %d 2: %d 3: %d 4: %d", cache[0].waveform, cache[1].waveform, cache[2].waveform, cache[3].waveform);
+
+#if MT32EMU_MONITOR_INSTRUMENTS == 1
+ synth->printDebug("%s (%s): Recached timbre", name, currentInstr);
+ for (int i = 0; i < 4; i++) {
+ synth->printDebug(" %d: play=%s, pcm=%s (%d), wave=%d", i, cache[i].playPartial ? "YES" : "NO", cache[i].PCMPartial ? "YES" : "NO", timbre->partial[i].wg.pcmwave, timbre->partial[i].wg.waveform);
+ }
+#endif
+}
+
+const char *Part::getName() const {
+ return name;
+}
+
+void Part::updateVolume() {
+ volumeMult = synth->tables.volumeMult[patchTemp->outlevel * expression / 127];
+}
+
+int Part::getVolume() const {
+ // FIXME: Use the mappings for this in the control ROM
+ return patchTemp->outlevel * 127 / 100;
+}
+
+void Part::setVolume(int midiVolume) {
+ // FIXME: Use the mappings for this in the control ROM
+ patchTemp->outlevel = (Bit8u)(midiVolume * 100 / 127);
+ updateVolume();
+ synth->printDebug("%s (%s): Set volume to %d", name, currentInstr, midiVolume);
+}
+
+void Part::setExpression(int midiExpression) {
+ expression = midiExpression;
+ updateVolume();
+}
+
+void RhythmPart::setPan(unsigned int midiPan)
+{
+ // FIXME:KG: This is unchangeable for drums (they always use drumPan), is that correct?
+ synth->printDebug("%s: Setting pan (%d) not supported on rhythm", name, midiPan);
+}
+
+void Part::setPan(unsigned int midiPan) {
+ // FIXME:KG: Tweaked this a bit so that we have a left 100%, centre and right 100%
+ // (But this makes the range somewhat skewed)
+ // Check against the real thing
+ // NOTE: Panning is inverted compared to GM.
+ if (midiPan < 64) {
+ volumesetting.leftvol = (Bit16s)(midiPan * 512);
+ volumesetting.rightvol = 32767;
+ } else if (midiPan == 64) {
+ volumesetting.leftvol = 32767;
+ volumesetting.rightvol = 32767;
+ } else {
+ volumesetting.rightvol = (Bit16s)((127 - midiPan) * 520);
+ volumesetting.leftvol = 32767;
+ }
+ patchTemp->panpot = (Bit8u)(midiPan * 14 / 127);
+ //synth->printDebug("%s (%s): Set pan to %d", name, currentInstr, panpot);
+}
+
+void RhythmPart::playNote(unsigned int key, int vel) {
+ if (key < 24 || key > 108)/*> 87 on MT-32)*/ {
+ synth->printDebug("%s: Attempted to play invalid key %d", name, key);
+ return;
+ }
+ int drumNum = key - 24;
+ int drumTimbreNum = rhythmTemp[drumNum].timbre;
+ if (drumTimbreNum >= 127) { // 94 on MT-32
+ synth->printDebug("%s: Attempted to play unmapped key %d", name, key);
+ return;
+ }
+ int absTimbreNum = drumTimbreNum + 128;
+ TimbreParam *timbre = &synth->mt32ram.timbres[absTimbreNum].timbre;
+ memcpy(currentInstr, timbre->common.name, 10);
+#if MT32EMU_MONITOR_INSTRUMENTS == 1
+ synth->printDebug("%s (%s): starting poly (drum %d, timbre %d) - Vel %d Key %d", name, currentInstr, drumNum, absTimbreNum, vel, key);
+#endif
+ if (drumCache[drumNum][0].dirty) {
+ cacheTimbre(drumCache[drumNum], timbre);
+ }
+ playPoly(drumCache[drumNum], key, MIDDLEC, vel);
+}
+
+void Part::playNote(unsigned int key, int vel) {
+ int freqNum = key;
+ if (freqNum < 12) {
+ synth->printDebug("%s (%s): Attempted to play invalid key %d < 12; moving up by octave", name, currentInstr, key);
+ freqNum += 12;
+ } else if (freqNum > 108) {
+ synth->printDebug("%s (%s): Attempted to play invalid key %d > 108; moving down by octave", name, currentInstr, key);
+ while (freqNum > 108) {
+ freqNum -= 12;
+ }
+ }
+ // POLY1 mode, Single Assign
+ // Haven't found any software that uses any of the other poly modes
+ // FIXME:KG: Should this also apply to rhythm?
+ for (unsigned int i = 0; i < MT32EMU_MAX_POLY; i++) {
+ if (polyTable[i].isActive() && (polyTable[i].key == key)) {
+ //AbortPoly(&polyTable[i]);
+ stopNote(key);
+ break;
+ }
+ }
+#if MT32EMU_MONITOR_INSTRUMENTS == 1
+ synth->printDebug("%s (%s): starting poly - Vel %d Key %d", name, currentInstr, vel, key);
+#endif
+ if (patchCache[0].dirty) {
+ cacheTimbre(patchCache, timbreTemp);
+ }
+ playPoly(patchCache, key, freqNum, vel);
+}
+
+void Part::playPoly(const PatchCache cache[4], unsigned int key, int freqNum, int vel) {
+ unsigned int needPartials = cache[0].partialCount;
+ unsigned int freePartials = synth->partialManager->getFreePartialCount();
+
+ if (freePartials < needPartials) {
+ if (!synth->partialManager->freePartials(needPartials - freePartials, partNum)) {
+ synth->printDebug("%s (%s): Insufficient free partials to play key %d (vel=%d); needed=%d, free=%d", name, currentInstr, key, vel, needPartials, synth->partialManager->getFreePartialCount());
+ return;
+ }
+ }
+ // Find free poly
+ int m;
+ for (m = 0; m < MT32EMU_MAX_POLY; m++) {
+ if (!polyTable[m].isActive()) {
+ break;
+ }
+ }
+ if (m == MT32EMU_MAX_POLY) {
+ synth->printDebug("%s (%s): No free poly to play key %d (vel %d)", name, currentInstr, key, vel);
+ return;
+ }
+
+ dpoly *tpoly = &polyTable[m];
+
+ tpoly->isPlaying = true;
+ tpoly->key = key;
+ tpoly->isDecay = false;
+ tpoly->freqnum = freqNum;
+ tpoly->vel = vel;
+ tpoly->pedalhold = false;
+
+ bool allnull = true;
+ for (int x = 0; x < 4; x++) {
+ if (cache[x].playPartial) {
+ tpoly->partials[x] = synth->partialManager->allocPartial(partNum);
+ allnull = false;
+ } else {
+ tpoly->partials[x] = NULL;
+ }
+ }
+
+ if (allnull)
+ synth->printDebug("%s (%s): No partials to play for this instrument", name, this->currentInstr);
+
+ tpoly->sustain = cache[0].sustain;
+ tpoly->volumeptr = &volumeMult;
+
+ for (int x = 0; x < 4; x++) {
+ if (tpoly->partials[x] != NULL) {
+ tpoly->partials[x]->startPartial(tpoly, &cache[x], tpoly->partials[cache[x].structurePair]);
+ tpoly->partials[x]->setBend(bend);
+ }
+ }
+}
+
+static void startDecayPoly(dpoly *tpoly) {
+ if (tpoly->isDecay) {
+ return;
+ }
+ tpoly->isDecay = true;
+
+ for (int t = 0; t < 4; t++) {
+ Partial *partial = tpoly->partials[t];
+ if (partial == NULL)
+ continue;
+ partial->startDecayAll();
+ }
+ tpoly->isPlaying = false;
+}
+
+void Part::allNotesOff() {
+ // Note: Unchecked on real MT-32, but the MIDI specification states that all notes off (0x7B)
+ // should treat the hold pedal as usual.
+ // All *sound* off (0x78) should stop notes immediately regardless of the hold pedal.
+ // The latter controller is not implemented on the MT-32 (according to the docs).
+ for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
+ dpoly *tpoly = &polyTable[q];
+ if (tpoly->isPlaying) {
+ if (holdpedal)
+ tpoly->pedalhold = true;
+ else if (tpoly->sustain)
+ startDecayPoly(tpoly);
+ }
+ }
+}
+
+void Part::allSoundOff() {
+ for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
+ dpoly *tpoly = &polyTable[q];
+ if (tpoly->isPlaying) {
+ startDecayPoly(tpoly);
+ }
+ }
+}
+
+void Part::stopPedalHold() {
+ for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
+ dpoly *tpoly;
+ tpoly = &polyTable[q];
+ if (tpoly->isActive() && tpoly->pedalhold)
+ stopNote(tpoly->key);
+ }
+}
+
+void Part::stopNote(unsigned int key) {
+ // Non-sustaining instruments ignore stop commands.
+ // They die away eventually anyway
+
+#if MT32EMU_MONITOR_INSTRUMENTS == 1
+ synth->printDebug("%s (%s): stopping key %d", name, currentInstr, key);
+#endif
+
+ if (key != 255) {
+ for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
+ dpoly *tpoly = &polyTable[q];
+ if (tpoly->isPlaying && tpoly->key == key) {
+ if (holdpedal)
+ tpoly->pedalhold = true;
+ else if (tpoly->sustain)
+ startDecayPoly(tpoly);
+ }
+ }
+ return;
+ }
+
+ // Find oldest poly... yes, the MT-32 can be reconfigured to kill different poly first
+ // This is simplest
+ int oldest = -1;
+ Bit32u oldage = 0;
+
+ for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
+ dpoly *tpoly = &polyTable[q];
+
+ if (tpoly->isPlaying && !tpoly->isDecay) {
+ if (tpoly->getAge() >= oldage) {
+ oldage = tpoly->getAge();
+ oldest = q;
+ }
+ }
+ }
+
+ if (oldest != -1) {
+ startDecayPoly(&polyTable[oldest]);
+ }
+}
+
+}
diff --git a/engines/sci/sfx/softseq/mt32/part.h b/engines/sci/sfx/softseq/mt32/part.h
new file mode 100644
index 0000000000..54c4999653
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/part.h
@@ -0,0 +1,113 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef MT32EMU_PART_H
+#define MT32EMU_PART_H
+
+namespace MT32Emu {
+
+class PartialManager;
+class Synth;
+
+class Part {
+private:
+ // Pointers to the areas of the MT-32's memory dedicated to this part (for parts 1-8)
+ MemParams::PatchTemp *patchTemp;
+ TimbreParam *timbreTemp;
+
+ // 0=Part 1, .. 7=Part 8, 8=Rhythm
+ unsigned int partNum;
+
+ bool holdpedal;
+
+ StereoVolume volumesetting;
+
+ PatchCache patchCache[4];
+
+ float bend; // -1.0 .. +1.0
+
+ dpoly polyTable[MT32EMU_MAX_POLY];
+
+ void abortPoly(dpoly *poly);
+
+ static int fixKeyfollow(int srckey);
+ static int fixBiaslevel(int srcpnt, int *dir);
+
+ void setPatch(const PatchParam *patch);
+
+protected:
+ Synth *synth;
+ char name[8]; // "Part 1".."Part 8", "Rhythm"
+ char currentInstr[11];
+ int expression;
+ Bit32u volumeMult;
+
+ void updateVolume();
+ void backupCacheToPartials(PatchCache cache[4]);
+ void cacheTimbre(PatchCache cache[4], const TimbreParam *timbre);
+ void playPoly(const PatchCache cache[4], unsigned int key, int freqNum, int vel);
+ const char *getName() const;
+
+public:
+ Part(Synth *synth, unsigned int usePartNum);
+ virtual ~Part() {}
+ virtual void playNote(unsigned int key, int vel);
+ void stopNote(unsigned int key);
+ void allNotesOff();
+ void allSoundOff();
+ int getVolume() const;
+ void setVolume(int midiVolume);
+ void setExpression(int midiExpression);
+ virtual void setPan(unsigned int midiPan);
+ virtual void setBend(unsigned int midiBend);
+ virtual void setModulation(unsigned int midiModulation);
+ virtual void setProgram(unsigned int midiProgram);
+ void setHoldPedal(bool pedalval);
+ void stopPedalHold();
+ virtual void refresh();
+ virtual void refreshTimbre(unsigned int absTimbreNum);
+ virtual void setTimbre(TimbreParam *timbre);
+ virtual unsigned int getAbsTimbreNum() const;
+ const char *getCurrentInstr() const;
+};
+
+class RhythmPart: public Part {
+ // Pointer to the area of the MT-32's memory dedicated to rhythm
+ const MemParams::RhythmTemp *rhythmTemp;
+
+ // This caches the timbres/settings in use by the rhythm part
+ PatchCache drumCache[85][4];
+ StereoVolume drumPan[85];
+public:
+ RhythmPart(Synth *synth, unsigned int usePartNum);
+ void refresh();
+ void refreshTimbre(unsigned int timbreNum);
+ void setTimbre(TimbreParam *timbre);
+ void playNote(unsigned int key, int vel);
+ unsigned int getAbsTimbreNum() const;
+ void setPan(unsigned int midiPan);
+ void setBend(unsigned int midiBend);
+ void setModulation(unsigned int midiModulation);
+ void setProgram(unsigned int patchNum);
+};
+
+}
+#endif
diff --git a/engines/sci/sfx/softseq/mt32/partial.cpp b/engines/sci/sfx/softseq/mt32/partial.cpp
new file mode 100644
index 0000000000..9d32282a82
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/partial.cpp
@@ -0,0 +1,960 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "mt32emu.h"
+
+#ifdef MACOSX
+// Older versions of Mac OS X didn't supply a powf function. To ensure
+// binary compatibility, we force using pow instead of powf (the only
+// potential drawback is that it might be a little bit slower).
+#define powf pow
+#endif
+
+#define FIXEDPOINT_UDIV(x, y, point) (((x) << (point)) / ((y)))
+#define FIXEDPOINT_SDIV(x, y, point) (((x) * (1 << point)) / ((y)))
+#define FIXEDPOINT_UMULT(x, y, point) (((x) * (y)) >> point)
+#define FIXEDPOINT_SMULT(x, y, point) (((x) * (y)) / (1 << point))
+
+using namespace MT32Emu;
+
+Partial::Partial(Synth *useSynth) {
+ this->synth = useSynth;
+ ownerPart = -1;
+ poly = NULL;
+ pair = NULL;
+#if MT32EMU_ACCURATENOTES == 1
+ for (int i = 0; i < 3; i++) {
+ noteLookupStorage.waveforms[i] = new Bit16s[65536];
+ }
+ noteLookup = &noteLookupStorage;
+#endif
+}
+
+Partial::~Partial() {
+#if MT32EMU_ACCURATENOTES == 1
+ for (int i = 0; i < 3; i++) {
+ delete[] noteLookupStorage.waveforms[i];
+ }
+ delete[] noteLookupStorage.wavTable;
+#endif
+}
+
+int Partial::getOwnerPart() const {
+ return ownerPart;
+}
+
+bool Partial::isActive() {
+ return ownerPart > -1;
+}
+
+const dpoly *Partial::getDpoly() const {
+ return this->poly;
+}
+
+void Partial::activate(int part) {
+ // This just marks the partial as being assigned to a part
+ ownerPart = part;
+}
+
+void Partial::deactivate() {
+ ownerPart = -1;
+ if (poly != NULL) {
+ for (int i = 0; i < 4; i++) {
+ if (poly->partials[i] == this) {
+ poly->partials[i] = NULL;
+ break;
+ }
+ }
+ if (pair != NULL) {
+ pair->pair = NULL;
+ }
+ }
+}
+
+void Partial::initKeyFollow(int key) {
+ // Setup partial keyfollow
+ // Note follow relative to middle C
+
+ // Calculate keyfollow for pitch
+#if 1
+ float rel = key == -1 ? 0.0f : (key - MIDDLEC);
+ float newPitch = rel * patchCache->pitchKeyfollow + patchCache->pitch + patchCache->pitchShift;
+ //FIXME:KG: Does it truncate the keyfollowed pitch to a semitone (towards MIDDLEC)?
+ //int newKey = (int)(rel * patchCache->pitchKeyfollow);
+ //float newPitch = newKey + patchCache->pitch + patchCache->pitchShift;
+#else
+ float rel = key == -1 ? 0.0f : (key + patchCache->pitchShift - MIDDLEC);
+ float newPitch = rel * patchCache->pitchKeyfollow + patchCache->pitch;
+#endif
+#if MT32EMU_ACCURATENOTES == 1
+ noteVal = newPitch;
+ synth->printDebug("key=%d, pitch=%f, pitchKeyfollow=%f, pitchShift=%f, newPitch=%f", key, patchCache->pitch, patchCache->pitchKeyfollow, patchCache->pitchShift, newPitch);
+#else
+ float newPitchInt;
+ float newPitchFract = modff(newPitch, &newPitchInt);
+ if (newPitchFract > 0.5f) {
+ newPitchInt += 1.0f;
+ newPitchFract -= 1.0f;
+ }
+ noteVal = (int)newPitchInt;
+ fineShift = (int)(powf(2.0f, newPitchFract / 12.0f) * 4096.0f);
+ synth->printDebug("key=%d, pitch=%f, pitchKeyfollow=%f, pitchShift=%f, newPitch=%f, noteVal=%d, fineShift=%d", key, patchCache->pitch, patchCache->pitchKeyfollow, patchCache->pitchShift, newPitch, noteVal, fineShift);
+#endif
+ // FIXME:KG: Raise/lower by octaves until in the supported range.
+ while (noteVal > HIGHEST_NOTE) // FIXME:KG: see tables.cpp: >108?
+ noteVal -= 12;
+ while (noteVal < LOWEST_NOTE) // FIXME:KG: see tables.cpp: <12?
+ noteVal += 12;
+ // Calculate keyfollow for filter
+ int keyfollow = ((key - MIDDLEC) * patchCache->filtkeyfollow) / 4096;
+ if (keyfollow > 108)
+ keyfollow = 108;
+ else if (keyfollow < -108)
+ keyfollow = -108;
+ filtVal = synth->tables.tvfKeyfollowMult[keyfollow + 108];
+ realVal = synth->tables.tvfKeyfollowMult[(noteVal - MIDDLEC) + 108];
+}
+
+int Partial::getKey() const {
+ if (poly == NULL) {
+ return -1;
+ } else {
+ return poly->key;
+ }
+}
+
+void Partial::startPartial(dpoly *usePoly, const PatchCache *useCache, Partial *pairPartial) {
+ if (usePoly == NULL || useCache == NULL) {
+ synth->printDebug("*** Error: Starting partial for owner %d, usePoly=%s, useCache=%s", ownerPart, usePoly == NULL ? "*** NULL ***" : "OK", useCache == NULL ? "*** NULL ***" : "OK");
+ return;
+ }
+ patchCache = useCache;
+ poly = usePoly;
+ mixType = patchCache->structureMix;
+ structurePosition = patchCache->structurePosition;
+
+ play = true;
+ initKeyFollow(poly->freqnum); // Initialises noteVal, filtVal and realVal
+#if MT32EMU_ACCURATENOTES == 0
+ noteLookup = &synth->tables.noteLookups[noteVal - LOWEST_NOTE];
+#else
+ Tables::initNote(synth, &noteLookupStorage, noteVal, (float)synth->myProp.sampleRate, synth->masterTune, synth->pcmWaves, NULL);
+#endif
+ keyLookup = &synth->tables.keyLookups[poly->freqnum - 12];
+
+ if (patchCache->PCMPartial) {
+ pcmNum = patchCache->pcm;
+ if (synth->controlROMMap->pcmCount > 128) {
+ // CM-32L, etc. support two "banks" of PCMs, selectable by waveform type parameter.
+ if (patchCache->waveform > 1) {
+ pcmNum += 128;
+ }
+ }
+ pcmWave = &synth->pcmWaves[pcmNum];
+ } else {
+ pcmWave = NULL;
+ }
+
+ lfoPos = 0;
+ pulsewidth = patchCache->pulsewidth + synth->tables.pwVelfollowAdd[patchCache->pwsens][poly->vel];
+ if (pulsewidth > 100) {
+ pulsewidth = 100;
+ } else if (pulsewidth < 0) {
+ pulsewidth = 0;
+ }
+
+ for (int e = 0; e < 3; e++) {
+ envs[e].envpos = 0;
+ envs[e].envstat = -1;
+ envs[e].envbase = 0;
+ envs[e].envdist = 0;
+ envs[e].envsize = 0;
+ envs[e].sustaining = false;
+ envs[e].decaying = false;
+ envs[e].prevlevel = 0;
+ envs[e].counter = 0;
+ envs[e].count = 0;
+ }
+ ampEnvVal = 0;
+ pitchEnvVal = 0;
+ pitchSustain = false;
+ loopPos = 0;
+ partialOff.pcmoffset = partialOff.pcmplace = 0;
+ pair = pairPartial;
+ useNoisePair = pairPartial == NULL && (mixType == 1 || mixType == 2);
+ age = 0;
+ alreadyOutputed = false;
+ memset(history,0,sizeof(history));
+}
+
+Bit16s *Partial::generateSamples(long length) {
+ if (!isActive() || alreadyOutputed) {
+ return NULL;
+ }
+ if (poly == NULL) {
+ synth->printDebug("*** ERROR: poly is NULL at Partial::generateSamples()!");
+ return NULL;
+ }
+
+ alreadyOutputed = true;
+
+ // Generate samples
+
+ Bit16s *partialBuf = &myBuffer[0];
+ Bit32u volume = *poly->volumeptr;
+ while (length--) {
+ Bit32s envval;
+ Bit32s sample = 0;
+ if (!envs[EnvelopeType_amp].sustaining) {
+ if (envs[EnvelopeType_amp].count <= 0) {
+ Bit32u ampval = getAmpEnvelope();
+ if (!play) {
+ deactivate();
+ break;
+ }
+ if (ampval > 100) {
+ ampval = 100;
+ }
+
+ ampval = synth->tables.volumeMult[ampval];
+ ampval = FIXEDPOINT_UMULT(ampval, synth->tables.tvaVelfollowMult[poly->vel][(int)patchCache->ampEnv.velosens], 8);
+ //if (envs[EnvelopeType_amp].sustaining)
+ ampEnvVal = ampval;
+ }
+ --envs[EnvelopeType_amp].count;
+ }
+
+ unsigned int lfoShift = 0x1000;
+ if (pitchSustain) {
+ // Calculate LFO position
+ // LFO does not kick in completely until pitch envelope sustains
+ if (patchCache->lfodepth > 0) {
+ lfoPos++;
+ if (lfoPos >= patchCache->lfoperiod)
+ lfoPos = 0;
+ int lfoatm = FIXEDPOINT_UDIV(lfoPos, patchCache->lfoperiod, 16);
+ int lfoatr = synth->tables.sintable[lfoatm];
+ lfoShift = synth->tables.lfoShift[patchCache->lfodepth][lfoatr];
+ }
+ } else {
+ // Calculate Pitch envelope
+ envval = getPitchEnvelope();
+ int pd = patchCache->pitchEnv.depth;
+ pitchEnvVal = synth->tables.pitchEnvVal[pd][envval];
+ }
+
+ int delta;
+
+ // Wrap positions or end if necessary
+ if (patchCache->PCMPartial) {
+ // PCM partial
+
+ delta = noteLookup->wavTable[pcmNum];
+ int len = pcmWave->len;
+ if (partialOff.pcmplace >= len) {
+ if (pcmWave->loop) {
+ //partialOff.pcmplace = partialOff.pcmoffset = 0;
+ partialOff.pcmplace %= len;
+ } else {
+ play = false;
+ deactivate();
+ break;
+ }
+ }
+ } else {
+ // Synthesis partial
+ delta = 0x10000;
+ partialOff.pcmplace %= (Bit16u)noteLookup->div2;
+ }
+
+ // Build delta for position of next sample
+ // Fix delta code
+ Bit32u tdelta = delta;
+#if MT32EMU_ACCURATENOTES == 0
+ tdelta = FIXEDPOINT_UMULT(tdelta, fineShift, 12);
+#endif
+ tdelta = FIXEDPOINT_UMULT(tdelta, pitchEnvVal, 12);
+ tdelta = FIXEDPOINT_UMULT(tdelta, lfoShift, 12);
+ tdelta = FIXEDPOINT_UMULT(tdelta, bendShift, 12);
+ delta = (int)tdelta;
+
+ // Get waveform - either PCM or synthesized sawtooth or square
+ if (ampEnvVal > 0) {
+ if (patchCache->PCMPartial) {
+ // Render PCM sample
+ int ra, rb, dist;
+ Bit32u taddr;
+ Bit32u pcmAddr = pcmWave->addr;
+ if (delta < 0x10000) {
+ // Linear sound interpolation
+ taddr = pcmAddr + partialOff.pcmplace;
+ ra = synth->pcmROMData[taddr];
+ taddr++;
+ if (taddr == pcmAddr + pcmWave->len) {
+ // Past end of PCM
+ if (pcmWave->loop) {
+ rb = synth->pcmROMData[pcmAddr];
+ } else {
+ rb = 0;
+ }
+ } else {
+ rb = synth->pcmROMData[taddr];
+ }
+ dist = rb - ra;
+ sample = (ra + ((dist * (Bit32s)(partialOff.pcmoffset >> 8)) >> 8));
+ } else {
+ // Sound decimation
+ // The right way to do it is to use a lowpass filter on the waveform before selecting
+ // a point. This is too slow. The following approximates this as fast as possible
+ int idelta = delta >> 16;
+ taddr = pcmAddr + partialOff.pcmplace;
+ ra = synth->pcmROMData[taddr++];
+ for (int ix = 0; ix < idelta - 1; ix++) {
+ if (taddr == pcmAddr + pcmWave->len) {
+ // Past end of PCM
+ if (pcmWave->loop) {
+ taddr = pcmAddr;
+ } else {
+ // Behave as if all subsequent samples were 0
+ break;
+ }
+ }
+ ra += synth->pcmROMData[taddr++];
+ }
+ sample = ra / idelta;
+ }
+ } else {
+ // Render synthesised sample
+ int toff = partialOff.pcmplace;
+ int minorplace = partialOff.pcmoffset >> 14;
+ Bit32s filterInput;
+ Bit32s filtval = getFiltEnvelope();
+
+ //synth->printDebug("Filtval: %d", filtval);
+
+ if ((patchCache->waveform & 1) == 0) {
+ // Square waveform. Made by combining two pregenerated bandlimited
+ // sawtooth waveforms
+ Bit32u ofsA = ((toff << 2) + minorplace) % noteLookup->waveformSize[0];
+ int width = FIXEDPOINT_UMULT(noteLookup->div2, synth->tables.pwFactor[pulsewidth], 7);
+ Bit32u ofsB = (ofsA + width) % noteLookup->waveformSize[0];
+ Bit16s pa = noteLookup->waveforms[0][ofsA];
+ Bit16s pb = noteLookup->waveforms[0][ofsB];
+ filterInput = pa - pb;
+ // Non-bandlimited squarewave
+ /*
+ ofs = FIXEDPOINT_UMULT(noteLookup->div2, synth->tables.pwFactor[patchCache->pulsewidth], 8);
+ if (toff < ofs)
+ sample = 1 * WGAMP;
+ else
+ sample = -1 * WGAMP;
+ */
+ } else {
+ // Sawtooth. Made by combining the full cosine and half cosine according
+ // to how it looks on the MT-32. What it really does it takes the
+ // square wave and multiplies it by a full cosine
+ int waveoff = (toff << 2) + minorplace;
+ if (toff < noteLookup->sawTable[pulsewidth])
+ filterInput = noteLookup->waveforms[1][waveoff % noteLookup->waveformSize[1]];
+ else
+ filterInput = noteLookup->waveforms[2][waveoff % noteLookup->waveformSize[2]];
+ // This is the correct way
+ // Seems slow to me (though bandlimited) -- doesn't seem to
+ // sound any better though
+ /*
+ //int pw = (patchCache->pulsewidth * pulsemod[filtval]) >> 8;
+
+ Bit32u ofs = toff % (noteLookup->div2 >> 1);
+
+ Bit32u ofs3 = toff + FIXEDPOINT_UMULT(noteLookup->div2, synth->tables.pwFactor[patchCache->pulsewidth], 9);
+ ofs3 = ofs3 % (noteLookup->div2 >> 1);
+
+ pa = noteLookup->waveforms[0][ofs];
+ pb = noteLookup->waveforms[0][ofs3];
+ sample = ((pa - pb) * noteLookup->waveforms[2][toff]) / 2;
+ */
+ }
+
+ //Very exact filter
+ if (filtval > ((FILTERGRAN * 15) / 16))
+ filtval = ((FILTERGRAN * 15) / 16);
+ sample = (Bit32s)(floorf((synth->iirFilter)((float)filterInput, &history[0], synth->tables.filtCoeff[filtval][(int)patchCache->filtEnv.resonance])) / synth->tables.resonanceFactor[patchCache->filtEnv.resonance]);
+ if (sample < -32768) {
+ synth->printDebug("Overdriven amplitude for %d: %d:=%d < -32768", patchCache->waveform, filterInput, sample);
+ sample = -32768;
+ }
+ else if (sample > 32767) {
+ synth->printDebug("Overdriven amplitude for %d: %d:=%d > 32767", patchCache->waveform, filterInput, sample);
+ sample = 32767;
+ }
+ }
+ }
+
+ // Add calculated delta to our waveform offset
+ Bit32u absOff = ((partialOff.pcmplace << 16) | partialOff.pcmoffset);
+ absOff += delta;
+ partialOff.pcmplace = (Bit16u)((absOff & 0xFFFF0000) >> 16);
+ partialOff.pcmoffset = (Bit16u)(absOff & 0xFFFF);
+
+ // Put volume envelope over generated sample
+ sample = FIXEDPOINT_SMULT(sample, ampEnvVal, 9);
+ sample = FIXEDPOINT_SMULT(sample, volume, 7);
+ envs[EnvelopeType_amp].envpos++;
+ envs[EnvelopeType_pitch].envpos++;
+ envs[EnvelopeType_filt].envpos++;
+
+ *partialBuf++ = (Bit16s)sample;
+ }
+ // We may have deactivated and broken out of the loop before the end of the buffer,
+ // if so then fill the remainder with 0s.
+ if (++length > 0)
+ memset(partialBuf, 0, length * 2);
+ return &myBuffer[0];
+}
+
+void Partial::setBend(float factor) {
+ if (!patchCache->useBender || factor == 0.0f) {
+ bendShift = 4096;
+ return;
+ }
+ // NOTE:KG: We can't do this smoothly with lookup tables, unless we use several MB.
+ // FIXME:KG: Bend should be influenced by pitch key-follow too, according to docs.
+ float bendSemitones = factor * patchCache->benderRange; // -24 .. 24
+ float mult = powf(2.0f, bendSemitones / 12.0f);
+ synth->printDebug("setBend(): factor=%f, benderRange=%f, semitones=%f, mult=%f\n", factor, patchCache->benderRange, bendSemitones, mult);
+ bendShift = (int)(mult * 4096.0f);
+}
+
+Bit16s *Partial::mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) {
+ if (buf1 == NULL)
+ return buf2;
+ if (buf2 == NULL)
+ return buf1;
+
+ Bit16s *outBuf = buf1;
+#if MT32EMU_USE_MMX >= 1
+ // KG: This seems to be fine
+ int donelen = i386_mixBuffers(buf1, buf2, len);
+ len -= donelen;
+ buf1 += donelen;
+ buf2 += donelen;
+#endif
+ while (len--) {
+ *buf1 = *buf1 + *buf2;
+ buf1++, buf2++;
+ }
+ return outBuf;
+}
+
+Bit16s *Partial::mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) {
+ if (buf1 == NULL)
+ return NULL;
+ if (buf2 == NULL) {
+ Bit16s *outBuf = buf1;
+ while (len--) {
+ if (*buf1 < -8192)
+ *buf1 = -8192;
+ else if (*buf1 > 8192)
+ *buf1 = 8192;
+ buf1++;
+ }
+ return outBuf;
+ }
+
+ Bit16s *outBuf = buf1;
+#if MT32EMU_USE_MMX >= 1
+ // KG: This seems to be fine
+ int donelen = i386_mixBuffersRingMix(buf1, buf2, len);
+ len -= donelen;
+ buf1 += donelen;
+ buf2 += donelen;
+#endif
+ while (len--) {
+ float a, b;
+ a = ((float)*buf1) / 8192.0f;
+ b = ((float)*buf2) / 8192.0f;
+ a = (a * b) + a;
+ if (a>1.0)
+ a = 1.0;
+ if (a<-1.0)
+ a = -1.0;
+ *buf1 = (Bit16s)(a * 8192.0f);
+ buf1++;
+ buf2++;
+ //buf1[i] = (Bit16s)(((Bit32s)buf1[i] * (Bit32s)buf2[i]) >> 10) + buf1[i];
+ }
+ return outBuf;
+}
+
+Bit16s *Partial::mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) {
+ if (buf1 == NULL) {
+ return NULL;
+ }
+ if (buf2 == NULL) {
+ return NULL;
+ }
+
+ Bit16s *outBuf = buf1;
+#if MT32EMU_USE_MMX >= 1
+ // FIXME:KG: Not really checked as working
+ int donelen = i386_mixBuffersRing(buf1, buf2, len);
+ len -= donelen;
+ buf1 += donelen;
+ buf2 += donelen;
+#endif
+ while (len--) {
+ float a, b;
+ a = ((float)*buf1) / 8192.0f;
+ b = ((float)*buf2) / 8192.0f;
+ a *= b;
+ if (a>1.0)
+ a = 1.0;
+ if (a<-1.0)
+ a = -1.0;
+ *buf1 = (Bit16s)(a * 8192.0f);
+ buf1++;
+ buf2++;
+ }
+ return outBuf;
+}
+
+void Partial::mixBuffersStereo(Bit16s *buf1, Bit16s *buf2, Bit16s *outBuf, int len) {
+ if (buf2 == NULL) {
+ while (len--) {
+ *outBuf++ = *buf1++;
+ *outBuf++ = 0;
+ }
+ } else if (buf1 == NULL) {
+ while (len--) {
+ *outBuf++ = 0;
+ *outBuf++ = *buf2++;
+ }
+ } else {
+ while (len--) {
+ *outBuf++ = *buf1++;
+ *outBuf++ = *buf2++;
+ }
+ }
+}
+
+bool Partial::produceOutput(Bit16s *partialBuf, long length) {
+ if (!isActive() || alreadyOutputed)
+ return false;
+ if (poly == NULL) {
+ synth->printDebug("*** ERROR: poly is NULL at Partial::produceOutput()!");
+ return false;
+ }
+
+ Bit16s *pairBuf = NULL;
+ // Check for dependant partial
+ if (pair != NULL) {
+ if (!pair->alreadyOutputed) {
+ // Note: pair may have become NULL after this
+ pairBuf = pair->generateSamples(length);
+ }
+ } else if (useNoisePair) {
+ // Generate noise for pairless ring mix
+ pairBuf = synth->tables.noiseBuf;
+ }
+
+ Bit16s *myBuf = generateSamples(length);
+
+ if (myBuf == NULL && pairBuf == NULL)
+ return false;
+
+ Bit16s *p1buf, *p2buf;
+
+ if (structurePosition == 0 || pairBuf == NULL) {
+ p1buf = myBuf;
+ p2buf = pairBuf;
+ } else {
+ p2buf = myBuf;
+ p1buf = pairBuf;
+ }
+
+ //synth->printDebug("mixType: %d", mixType);
+
+ Bit16s *mixedBuf;
+ switch (mixType) {
+ case 0:
+ // Standard sound mix
+ mixedBuf = mixBuffers(p1buf, p2buf, length);
+ break;
+
+ case 1:
+ // Ring modulation with sound mix
+ mixedBuf = mixBuffersRingMix(p1buf, p2buf, length);
+ break;
+
+ case 2:
+ // Ring modulation alone
+ mixedBuf = mixBuffersRing(p1buf, p2buf, length);
+ break;
+
+ case 3:
+ // Stereo mixing. One partial to one speaker channel, one to another.
+ // FIXME:KG: Surely we should be multiplying by the left/right volumes here?
+ mixBuffersStereo(p1buf, p2buf, partialBuf, length);
+ return true;
+
+ default:
+ mixedBuf = mixBuffers(p1buf, p2buf, length);
+ break;
+ }
+
+ if (mixedBuf == NULL)
+ return false;
+
+ Bit16s leftvol, rightvol;
+ leftvol = patchCache->pansetptr->leftvol;
+ rightvol = patchCache->pansetptr->rightvol;
+
+#if MT32EMU_USE_MMX >= 2
+ // FIXME:KG: This appears to introduce crackle
+ int donelen = i386_partialProductOutput(length, leftvol, rightvol, partialBuf, mixedBuf);
+ length -= donelen;
+ mixedBuf += donelen;
+ partialBuf += donelen * 2;
+#endif
+ while (length--) {
+ *partialBuf++ = (Bit16s)(((Bit32s)*mixedBuf * (Bit32s)leftvol) >> 15);
+ *partialBuf++ = (Bit16s)(((Bit32s)*mixedBuf * (Bit32s)rightvol) >> 15);
+ mixedBuf++;
+ }
+ return true;
+}
+
+Bit32s Partial::getFiltEnvelope() {
+ int reshigh;
+
+ int cutoff, depth;
+
+ EnvelopeStatus *tStat = &envs[EnvelopeType_filt];
+
+ if (tStat->decaying) {
+ reshigh = tStat->envbase;
+ reshigh = (reshigh + ((tStat->envdist * tStat->envpos) / tStat->envsize));
+ if (tStat->envpos >= tStat->envsize)
+ reshigh = 0;
+ } else {
+ if (tStat->envstat==4) {
+ reshigh = patchCache->filtsustain;
+ if (!poly->sustain) {
+ startDecay(EnvelopeType_filt, reshigh);
+ }
+ } else {
+ if ((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) {
+ if (tStat->envstat==-1)
+ tStat->envbase = 0;
+ else
+ tStat->envbase = patchCache->filtEnv.envlevel[tStat->envstat];
+ tStat->envstat++;
+ tStat->envpos = 0;
+ if (tStat->envstat == 3) {
+ tStat->envsize = synth->tables.envTime[(int)patchCache->filtEnv.envtime[tStat->envstat]];
+ } else {
+ Bit32u envTime = (int)patchCache->filtEnv.envtime[tStat->envstat];
+ if (tStat->envstat > 1) {
+ int envDiff = abs(patchCache->filtEnv.envlevel[tStat->envstat] - patchCache->filtEnv.envlevel[tStat->envstat - 1]);
+ if (envTime > synth->tables.envDeltaMaxTime[envDiff]) {
+ envTime = synth->tables.envDeltaMaxTime[envDiff];
+ }
+ }
+
+ tStat->envsize = (synth->tables.envTime[envTime] * keyLookup->envTimeMult[(int)patchCache->filtEnv.envtkf]) >> 8;
+ }
+
+ tStat->envsize++;
+ tStat->envdist = patchCache->filtEnv.envlevel[tStat->envstat] - tStat->envbase;
+ }
+
+ reshigh = tStat->envbase;
+ reshigh = (reshigh + ((tStat->envdist * tStat->envpos) / tStat->envsize));
+
+ }
+ tStat->prevlevel = reshigh;
+ }
+
+ cutoff = patchCache->filtEnv.cutoff;
+
+ //if (patchCache->waveform==1) reshigh = (reshigh * 3) >> 2;
+
+ depth = patchCache->filtEnv.envdepth;
+
+ //int sensedep = (depth * 127-patchCache->filtEnv.envsense) >> 7;
+ depth = FIXEDPOINT_UMULT(depth, synth->tables.tvfVelfollowMult[poly->vel][(int)patchCache->filtEnv.envsense], 8);
+
+ int bias = patchCache->tvfbias;
+ int dist;
+
+ if (bias != 0) {
+ //FIXME:KG: Is this really based on pitch (as now), or key pressed?
+ //synth->printDebug("Cutoff before %d", cutoff);
+ if (patchCache->tvfdir == 0) {
+ if (noteVal < bias) {
+ dist = bias - noteVal;
+ cutoff = FIXEDPOINT_UMULT(cutoff, synth->tables.tvfBiasMult[patchCache->tvfblevel][dist], 8);
+ }
+ } else {
+ // > Bias
+ if (noteVal > bias) {
+ dist = noteVal - bias;
+ cutoff = FIXEDPOINT_UMULT(cutoff, synth->tables.tvfBiasMult[patchCache->tvfblevel][dist], 8);
+ }
+
+ }
+ //synth->printDebug("Cutoff after %d", cutoff);
+ }
+
+ depth = (depth * keyLookup->envDepthMult[patchCache->filtEnv.envdkf]) >> 8;
+ reshigh = (reshigh * depth) >> 7;
+
+ Bit32s tmp;
+
+ cutoff *= filtVal;
+ cutoff /= realVal; //FIXME:KG: With filter keyfollow 0, this makes no sense. What's correct?
+
+ reshigh *= filtVal;
+ reshigh /= realVal; //FIXME:KG: As above for cutoff
+
+ if (patchCache->waveform == 1) {
+ reshigh = (reshigh * 65) / 100;
+ }
+
+ if (cutoff > 100)
+ cutoff = 100;
+ else if (cutoff < 0)
+ cutoff = 0;
+ if (reshigh > 100)
+ reshigh = 100;
+ else if (reshigh < 0)
+ reshigh = 0;
+ tmp = noteLookup->nfiltTable[cutoff][reshigh];
+ //tmp *= keyfollow;
+ //tmp /= realfollow;
+
+ //synth->printDebug("Cutoff %d, tmp %d, freq %d", cutoff, tmp, tmp * 256);
+ return tmp;
+}
+
+bool Partial::shouldReverb() {
+ if (!isActive())
+ return false;
+ return patchCache->reverb;
+}
+
+Bit32u Partial::getAmpEnvelope() {
+ Bit32s tc;
+
+ EnvelopeStatus *tStat = &envs[EnvelopeType_amp];
+
+ if (!play)
+ return 0;
+
+ if (tStat->decaying) {
+ tc = tStat->envbase;
+ tc += (tStat->envdist * tStat->envpos) / tStat->envsize;
+ if (tc < 0)
+ tc = 0;
+ if ((tStat->envpos >= tStat->envsize) || (tc == 0)) {
+ play = false;
+ // Don't have to worry about prevlevel storage or anything, this partial's about to die
+ return 0;
+ }
+ } else {
+ if ((tStat->envstat == -1) || (tStat->envpos >= tStat->envsize)) {
+ if (tStat->envstat == -1)
+ tStat->envbase = 0;
+ else
+ tStat->envbase = patchCache->ampEnv.envlevel[tStat->envstat];
+ tStat->envstat++;
+ tStat->envpos = 0;
+ if (tStat->envstat == 4) {
+ //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
+ tc = patchCache->ampEnv.envlevel[3];
+ if (!poly->sustain)
+ startDecay(EnvelopeType_amp, tc);
+ else
+ tStat->sustaining = true;
+ goto PastCalc;
+ }
+ Bit8u targetLevel = patchCache->ampEnv.envlevel[tStat->envstat];
+ tStat->envdist = targetLevel - tStat->envbase;
+ Bit32u envTime = patchCache->ampEnv.envtime[tStat->envstat];
+ if (targetLevel == 0) {
+ tStat->envsize = synth->tables.envDecayTime[envTime];
+ } else {
+ int envLevelDelta = abs(tStat->envdist);
+ if (envTime > synth->tables.envDeltaMaxTime[envLevelDelta]) {
+ envTime = synth->tables.envDeltaMaxTime[envLevelDelta];
+ }
+ tStat->envsize = synth->tables.envTime[envTime];
+ }
+
+ // Time keyfollow is used by all sections of the envelope (confirmed on CM-32L)
+ tStat->envsize = FIXEDPOINT_UMULT(tStat->envsize, keyLookup->envTimeMult[(int)patchCache->ampEnv.envtkf], 8);
+
+ switch (tStat->envstat) {
+ case 0:
+ //Spot for velocity time follow
+ //Only used for first attack
+ tStat->envsize = FIXEDPOINT_UMULT(tStat->envsize, synth->tables.envTimeVelfollowMult[(int)patchCache->ampEnv.envvkf][poly->vel], 8);
+ //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
+ break;
+ case 1:
+ case 2:
+ case 3:
+ //synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
+ break;
+ default:
+ synth->printDebug("Invalid TVA envelope number %d hit!", tStat->envstat);
+ break;
+ }
+
+ tStat->envsize++;
+
+ if (tStat->envdist != 0) {
+ tStat->counter = abs(tStat->envsize / tStat->envdist);
+ //synth->printDebug("Pos %d, envsize %d envdist %d", tStat->envstat, tStat->envsize, tStat->envdist);
+ } else {
+ tStat->counter = 0;
+ //synth->printDebug("Pos %d, envsize %d envdist %d", tStat->envstat, tStat->envsize, tStat->envdist);
+ }
+ }
+ tc = tStat->envbase;
+ tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize));
+ tStat->count = tStat->counter;
+PastCalc:
+ tc = (tc * (Bit32s)patchCache->ampEnv.level) / 100;
+ }
+
+ // Prevlevel storage is bottle neck
+ tStat->prevlevel = tc;
+
+ //Bias level crap stuff now
+
+ for (int i = 0; i < 2; i++) {
+ if (patchCache->ampblevel[i]!=0) {
+ int bias = patchCache->ampbias[i];
+ if (patchCache->ampdir[i]==0) {
+ // < Bias
+ if (noteVal < bias) {
+ int dist = bias - noteVal;
+ tc = FIXEDPOINT_UMULT(tc, synth->tables.tvaBiasMult[patchCache->ampblevel[i]][dist], 8);
+ }
+ } else {
+ // > Bias
+ if (noteVal > bias) {
+ int dist = noteVal - bias;
+ tc = FIXEDPOINT_UMULT(tc, synth->tables.tvaBiasMult[patchCache->ampblevel[i]][dist], 8);
+ }
+ }
+ }
+ }
+ if (tc < 0) {
+ synth->printDebug("*** ERROR: tc < 0 (%d) at getAmpEnvelope()", tc);
+ tc = 0;
+ }
+ return (Bit32u)tc;
+}
+
+Bit32s Partial::getPitchEnvelope() {
+ EnvelopeStatus *tStat = &envs[EnvelopeType_pitch];
+
+ Bit32s tc;
+ pitchSustain = false;
+ if (tStat->decaying) {
+ if (tStat->envpos >= tStat->envsize)
+ tc = patchCache->pitchEnv.level[4];
+ else {
+ tc = tStat->envbase;
+ tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize));
+ }
+ } else {
+ if (tStat->envstat==3) {
+ tc = patchCache->pitchsustain;
+ if (poly->sustain)
+ pitchSustain = true;
+ else
+ startDecay(EnvelopeType_pitch, tc);
+ } else {
+ if ((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) {
+ tStat->envstat++;
+
+ tStat->envbase = patchCache->pitchEnv.level[tStat->envstat];
+
+ Bit32u envTime = patchCache->pitchEnv.time[tStat->envstat];
+ int envDiff = abs(patchCache->pitchEnv.level[tStat->envstat] - patchCache->pitchEnv.level[tStat->envstat + 1]);
+ if (envTime > synth->tables.envDeltaMaxTime[envDiff]) {
+ envTime = synth->tables.envDeltaMaxTime[envDiff];
+ }
+
+ tStat->envsize = (synth->tables.envTime[envTime] * keyLookup->envTimeMult[(int)patchCache->pitchEnv.timekeyfollow]) >> 8;
+
+ tStat->envpos = 0;
+ tStat->envsize++;
+ tStat->envdist = patchCache->pitchEnv.level[tStat->envstat + 1] - tStat->envbase;
+ }
+ tc = tStat->envbase;
+ tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize));
+ }
+ tStat->prevlevel = tc;
+ }
+ return tc;
+}
+
+void Partial::startDecayAll() {
+ startDecay(EnvelopeType_amp, envs[EnvelopeType_amp].prevlevel);
+ startDecay(EnvelopeType_filt, envs[EnvelopeType_filt].prevlevel);
+ startDecay(EnvelopeType_pitch, envs[EnvelopeType_pitch].prevlevel);
+ pitchSustain = false;
+}
+
+void Partial::startDecay(EnvelopeType envnum, Bit32s startval) {
+ EnvelopeStatus *tStat = &envs[envnum];
+
+ tStat->sustaining = false;
+ tStat->decaying = true;
+ tStat->envpos = 0;
+ tStat->envbase = startval;
+
+ switch (envnum) {
+ case EnvelopeType_amp:
+ tStat->envsize = FIXEDPOINT_UMULT(synth->tables.envDecayTime[(int)patchCache->ampEnv.envtime[4]], keyLookup->envTimeMult[(int)patchCache->ampEnv.envtkf], 8);
+ tStat->envdist = -startval;
+ break;
+ case EnvelopeType_filt:
+ tStat->envsize = FIXEDPOINT_UMULT(synth->tables.envDecayTime[(int)patchCache->filtEnv.envtime[4]], keyLookup->envTimeMult[(int)patchCache->filtEnv.envtkf], 8);
+ tStat->envdist = -startval;
+ break;
+ case EnvelopeType_pitch:
+ tStat->envsize = FIXEDPOINT_UMULT(synth->tables.envDecayTime[(int)patchCache->pitchEnv.time[3]], keyLookup->envTimeMult[(int)patchCache->pitchEnv.timekeyfollow], 8);
+ tStat->envdist = patchCache->pitchEnv.level[4] - startval;
+ break;
+ default:
+ break;
+ }
+ tStat->envsize++;
+}
diff --git a/engines/sci/sfx/softseq/mt32/partial.h b/engines/sci/sfx/softseq/mt32/partial.h
new file mode 100644
index 0000000000..93d8bcd985
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/partial.h
@@ -0,0 +1,148 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef MT32EMU_PARTIAL_H
+#define MT32EMU_PARTIAL_H
+
+namespace MT32Emu {
+
+class Synth;
+struct NoteLookup;
+
+enum EnvelopeType {
+ EnvelopeType_amp = 0,
+ EnvelopeType_filt = 1,
+ EnvelopeType_pitch = 2
+};
+
+struct EnvelopeStatus {
+ Bit32s envpos;
+ Bit32s envstat;
+ Bit32s envbase;
+ Bit32s envdist;
+ Bit32s envsize;
+
+ bool sustaining;
+ bool decaying;
+ Bit32s prevlevel;
+
+ Bit32s counter;
+ Bit32s count;
+};
+
+// Class definition of MT-32 partials. 32 in all.
+class Partial {
+private:
+ Synth *synth;
+
+ int ownerPart; // -1 if unassigned
+ int mixType;
+ int structurePosition; // 0 or 1 of a structure pair
+ bool useNoisePair;
+
+ Bit16s myBuffer[MAX_SAMPLE_OUTPUT];
+
+ // Keyfollowed note value
+#if MT32EMU_ACCURATENOTES == 1
+ NoteLookup noteLookupStorage;
+ float noteVal;
+#else
+ int noteVal;
+ int fineShift;
+#endif
+ const NoteLookup *noteLookup; // LUTs for this noteVal
+ const KeyLookup *keyLookup; // LUTs for the clamped (12..108) key
+
+ // Keyfollowed filter values
+ int realVal;
+ int filtVal;
+
+ // Only used for PCM partials
+ int pcmNum;
+ PCMWaveEntry *pcmWave;
+
+ int pulsewidth;
+
+ Bit32u lfoPos;
+ soundaddr partialOff;
+
+ Bit32u ampEnvVal;
+ Bit32u pitchEnvVal;
+
+ float history[32];
+
+ bool pitchSustain;
+
+ int loopPos;
+
+ dpoly *poly;
+
+ int bendShift;
+
+ Bit16s *mixBuffers(Bit16s *buf1, Bit16s *buf2, int len);
+ Bit16s *mixBuffersRingMix(Bit16s *buf1, Bit16s *buf2, int len);
+ Bit16s *mixBuffersRing(Bit16s *buf1, Bit16s *buf2, int len);
+ void mixBuffersStereo(Bit16s *buf1, Bit16s *buf2, Bit16s *outBuf, int len);
+
+ Bit32s getFiltEnvelope();
+ Bit32u getAmpEnvelope();
+ Bit32s getPitchEnvelope();
+
+ void initKeyFollow(int freqNum);
+
+public:
+ const PatchCache *patchCache;
+ EnvelopeStatus envs[3];
+ bool play;
+
+ PatchCache cachebackup;
+
+ Partial *pair;
+ bool alreadyOutputed;
+ Bit32u age;
+
+ Partial(Synth *synth);
+ ~Partial();
+
+ int getOwnerPart() const;
+ int getKey() const;
+ const dpoly *getDpoly() const;
+ bool isActive();
+ void activate(int part);
+ void deactivate(void);
+ void startPartial(dpoly *usePoly, const PatchCache *useCache, Partial *pairPartial);
+ void startDecay(EnvelopeType envnum, Bit32s startval);
+ void startDecayAll();
+ void setBend(float factor);
+ bool shouldReverb();
+
+ // Returns true only if data written to buffer
+ // This function (unlike the one below it) returns processed stereo samples
+ // made from combining this single partial with its pair, if it has one.
+ bool produceOutput(Bit16s * partialBuf, long length);
+
+ // This function produces mono sample output using the partial's private internal buffer
+ Bit16s *generateSamples(long length);
+};
+
+}
+
+#endif
diff --git a/engines/sci/sfx/softseq/mt32/partialManager.cpp b/engines/sci/sfx/softseq/mt32/partialManager.cpp
new file mode 100644
index 0000000000..3d3b6302db
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/partialManager.cpp
@@ -0,0 +1,272 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <string.h>
+
+#include "mt32emu.h"
+
+using namespace MT32Emu;
+
+PartialManager::PartialManager(Synth *useSynth) {
+ this->synth = useSynth;
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)
+ partialTable[i] = new Partial(synth);
+}
+
+PartialManager::~PartialManager(void) {
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)
+ delete partialTable[i];
+}
+
+void PartialManager::getPerPartPartialUsage(int usage[9]) {
+ memset(usage, 0, 9 * sizeof (int));
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (partialTable[i]->isActive())
+ usage[partialTable[i]->getOwnerPart()]++;
+ }
+}
+
+void PartialManager::clearAlreadyOutputed() {
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)
+ partialTable[i]->alreadyOutputed = false;
+}
+
+void PartialManager::ageAll() {
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)
+ partialTable[i]->age++;
+}
+
+bool PartialManager::shouldReverb(int i) {
+ return partialTable[i]->shouldReverb();
+}
+
+bool PartialManager::produceOutput(int i, Bit16s *buffer, Bit32u bufferLength) {
+ return partialTable[i]->produceOutput(buffer, bufferLength);
+}
+
+void PartialManager::deactivateAll() {
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ partialTable[i]->deactivate();
+ }
+}
+
+unsigned int PartialManager::setReserve(Bit8u *rset) {
+ unsigned int pr = 0;
+ for (int x = 0; x < 9; x++) {
+ for (int y = 0; y < rset[x]; y++) {
+ partialReserveTable[pr] = x;
+ pr++;
+ }
+ }
+ return pr;
+}
+
+Partial *PartialManager::allocPartial(int partNum) {
+ Partial *outPartial = NULL;
+
+ // Use the first inactive partial reserved for the specified part (if there are any)
+ // Otherwise, use the last inactive partial, if any
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (!partialTable[i]->isActive()) {
+ outPartial = partialTable[i];
+ if (partialReserveTable[i] == partNum)
+ break;
+ }
+ }
+ if (outPartial != NULL) {
+ outPartial->activate(partNum);
+ outPartial->age = 0;
+ }
+ return outPartial;
+}
+
+unsigned int PartialManager::getFreePartialCount(void) {
+ int count = 0;
+ memset(partialPart, 0, sizeof(partialPart));
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (!partialTable[i]->isActive())
+ count++;
+ else
+ partialPart[partialTable[i]->getOwnerPart()]++;
+ }
+ return count;
+}
+
+/*
+bool PartialManager::freePartials(unsigned int needed, int partNum) {
+ int i;
+ int myPartPrior = (int)mt32ram.system.reserveSettings[partNum];
+ if (myPartPrior<partialPart[partNum]) {
+ //This can have more parts, must kill off those with less priority
+ int most, mostPart;
+ while (needed > 0) {
+ int selectPart = -1;
+ //Find the worst offender with more partials than allocated and kill them
+ most = -1;
+ mostPart = -1;
+ int diff;
+
+ for (i=0;i<9;i++) {
+ diff = partialPart[i] - (int)mt32ram.system.reserveSettings[i];
+
+ if (diff>0) {
+ if (diff>most) {
+ most = diff;
+ mostPart = i;
+ }
+ }
+ }
+ selectPart = mostPart;
+ if (selectPart == -1) {
+ // All parts are within the allocated limits, you suck
+ // Look for first partial not of this part that's decaying perhaps?
+ return false;
+ }
+ bool found;
+ int oldest;
+ int oldnum;
+ while (partialPart[selectPart] > (int)mt32ram.system.reserveSettings[selectPart]) {
+ oldest = -1;
+ oldnum = -1;
+ found = false;
+ for (i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (partialTable[i]->isActive) {
+ if (partialTable[i]->ownerPart == selectPart) {
+ found = true;
+ if (partialTable[i]->age > oldest) {
+ oldest = partialTable[i]->age;
+ oldnum = i;
+ }
+ }
+ }
+ }
+ if (!found) break;
+ partialTable[oldnum]->deactivate();
+ --partialPart[selectPart];
+ --needed;
+ }
+
+ }
+ return true;
+
+ } else {
+ //This part has reached its max, must kill off its own
+ bool found;
+ int oldest;
+ int oldnum;
+ while (needed > 0) {
+ oldest = -1;
+ oldnum = -1;
+ found = false;
+ for (i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (partialTable[i]->isActive) {
+ if (partialTable[i]->ownerPart == partNum) {
+ found = true;
+ if (partialTable[i]->age > oldest) {
+ oldest = partialTable[i]->age;
+ oldnum = i;
+ }
+ }
+ }
+ }
+ if (!found) break;
+ partialTable[oldnum]->deactivate();
+ --needed;
+ }
+ // Couldn't free enough partials, sorry
+ if (needed>0) return false;
+ return true;
+ }
+
+}
+*/
+bool PartialManager::freePartials(unsigned int needed, int partNum) {
+ if (needed == 0) {
+ return true;
+ }
+ // Reclaim partials reserved for this part
+ // Kill those that are already decaying first
+ /*
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (partialReserveTable[i] == partNum) {
+ if (partialTable[i]->ownerPart != partNum) {
+ if (partialTable[i]->partCache->envs[AMPENV].decaying) {
+ partialTable[i]->isActive = false;
+ --needed;
+ if (needed == 0)
+ return true;
+ }
+ }
+ }
+ }*/
+ // Then kill those with the lowest part priority -- oldest at the moment
+ while (needed > 0) {
+ Bit32u prior = 0;
+ int priornum = -1;
+
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (partialReserveTable[i] == partNum && partialTable[i]->isActive() && partialTable[i]->getOwnerPart() != partNum) {
+ /*
+ if (mt32ram.system.reserveSettings[partialTable[i]->ownerPart] < prior) {
+ prior = mt32ram.system.reserveSettings[partialTable[i]->ownerPart];
+ priornum = i;
+ }*/
+ if (partialTable[i]->age >= prior) {
+ prior = partialTable[i]->age;
+ priornum = i;
+ }
+ }
+ }
+ if (priornum != -1) {
+ partialTable[priornum]->deactivate();
+ --needed;
+ } else {
+ break;
+ }
+ }
+
+ // Kill off the oldest partials within this part
+ while (needed > 0) {
+ Bit32u oldest = 0;
+ int oldlist = -1;
+ for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (partialTable[i]->getOwnerPart() == partNum && partialTable[i]->isActive()) {
+ if (partialTable[i]->age >= oldest) {
+ oldest = partialTable[i]->age;
+ oldlist = i;
+ }
+ }
+ }
+ if (oldlist != -1) {
+ partialTable[oldlist]->deactivate();
+ --needed;
+ } else {
+ break;
+ }
+ }
+ return needed == 0;
+}
+
+const Partial *PartialManager::getPartial(unsigned int partialNum) const {
+ if (partialNum > MT32EMU_MAX_PARTIALS - 1)
+ return NULL;
+ return partialTable[partialNum];
+}
diff --git a/engines/sci/sfx/softseq/mt32/partialManager.h b/engines/sci/sfx/softseq/mt32/partialManager.h
new file mode 100644
index 0000000000..b10f93ff02
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/partialManager.h
@@ -0,0 +1,56 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef MT32EMU_PARTIALMANAGER_H
+#define MT32EMU_PARTIALMANAGER_H
+
+namespace MT32Emu {
+
+class Synth;
+
+class PartialManager {
+private:
+ Synth *synth; // Only used for sending debug output
+
+ Partial *partialTable[MT32EMU_MAX_PARTIALS];
+ Bit32s partialReserveTable[MT32EMU_MAX_PARTIALS];
+ Bit32s partialPart[9]; // The count of partials played per part
+
+public:
+
+ PartialManager(Synth *synth);
+ ~PartialManager();
+ Partial *allocPartial(int partNum);
+ unsigned int getFreePartialCount(void);
+ bool freePartials(unsigned int needed, int partNum);
+ unsigned int setReserve(Bit8u *rset);
+ void deactivateAll();
+ void ageAll();
+ bool produceOutput(int i, Bit16s *buffer, Bit32u bufferLength);
+ bool shouldReverb(int i);
+ void clearAlreadyOutputed();
+ void getPerPartPartialUsage(int usage[9]);
+ const Partial *getPartial(unsigned int partialNum) const;
+};
+
+}
+
+#endif
diff --git a/engines/sci/sfx/softseq/mt32/structures.h b/engines/sci/sfx/softseq/mt32/structures.h
new file mode 100644
index 0000000000..ef58c1d20f
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/structures.h
@@ -0,0 +1,284 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef MT32EMU_STRUCTURES_H
+#define MT32EMU_STRUCTURES_H
+
+namespace MT32Emu {
+
+const unsigned int MAX_SAMPLE_OUTPUT = 4096;
+
+// MT32EMU_MEMADDR() converts from sysex-padded, MT32EMU_SYSEXMEMADDR converts to it
+// Roland provides documentation using the sysex-padded addresses, so we tend to use that in code and output
+#define MT32EMU_MEMADDR(x) ((((x) & 0x7f0000) >> 2) | (((x) & 0x7f00) >> 1) | ((x) & 0x7f))
+#define MT32EMU_SYSEXMEMADDR(x) ((((x) & 0x1FC000) << 2) | (((x) & 0x3F80) << 1) | ((x) & 0x7f))
+
+#ifdef _MSC_VER
+#define MT32EMU_ALIGN_PACKED __declspec(align(1))
+typedef unsigned __int64 Bit64u;
+typedef signed __int64 Bit64s;
+#else
+#define MT32EMU_ALIGN_PACKED __attribute__((packed))
+typedef unsigned long long Bit64u;
+typedef signed long long Bit64s;
+#endif
+
+typedef unsigned int Bit32u;
+typedef signed int Bit32s;
+typedef unsigned short int Bit16u;
+typedef signed short int Bit16s;
+typedef unsigned char Bit8u;
+typedef signed char Bit8s;
+
+// The following structures represent the MT-32's memory
+// Since sysex allows this memory to be written to in blocks of bytes,
+// we keep this packed so that we can copy data into the various
+// banks directly
+#if defined(_MSC_VER) || defined (__MINGW32__)
+#pragma pack(push, 1)
+#else
+#pragma pack(1)
+#endif
+
+struct TimbreParam {
+ struct commonParam {
+ char name[10];
+ Bit8u pstruct12; // 1&2 0-12 (1-13)
+ Bit8u pstruct34; // #3&4 0-12 (1-13)
+ Bit8u pmute; // 0-15 (0000-1111)
+ Bit8u nosustain; // 0-1(Normal, No sustain)
+ } MT32EMU_ALIGN_PACKED common;
+
+ struct partialParam {
+ struct wgParam {
+ Bit8u coarse; // 0-96 (C1,C#1-C9)
+ Bit8u fine; // 0-100 (-50 to +50 (cents?))
+ Bit8u keyfollow; // 0-16 (-1,-1/2,0,1,1/8,1/4,3/8,1/2,5/8,3/4,7/8,1,5/4,3/2,2.s1,s2)
+ Bit8u bender; // 0,1 (ON/OFF)
+ Bit8u waveform; // MT-32: 0-1 (SQU/SAW); LAPC-I: WG WAVEFORM/PCM BANK 0 - 3 (SQU/1, SAW/1, SQU/2, SAW/2)
+ Bit8u pcmwave; // 0-127 (1-128)
+ Bit8u pulsewid; // 0-100
+ Bit8u pwvelo; // 0-14 (-7 - +7)
+ } MT32EMU_ALIGN_PACKED wg;
+
+ struct envParam {
+ Bit8u depth; // 0-10
+ Bit8u sensitivity; // 1-100
+ Bit8u timekeyfollow; // 0-4
+ Bit8u time[4]; // 1-100
+ Bit8u level[5]; // 1-100 (-50 - +50)
+ } MT32EMU_ALIGN_PACKED env;
+
+ struct lfoParam {
+ Bit8u rate; // 0-100
+ Bit8u depth; // 0-100
+ Bit8u modsense; // 0-100
+ } MT32EMU_ALIGN_PACKED lfo;
+
+ struct tvfParam {
+ Bit8u cutoff; // 0-100
+ Bit8u resonance; // 0-30
+ Bit8u keyfollow; // 0-16 (-1,-1/2,1/4,0,1,1/8,1/4,3/8,1/2,5/8,3/2,7/8,1,5/4,3/2,2,s1,s2)
+ Bit8u biaspoint; // 0-127 (<1A-<7C >1A-7C)
+ Bit8u biaslevel; // 0-14 (-7 - +7)
+ Bit8u envdepth; // 0-100
+ Bit8u envsense; // 0-100
+ Bit8u envdkf; // DEPTH KEY FOLL0W 0-4
+ Bit8u envtkf; // TIME KEY FOLLOW 0-4
+ Bit8u envtime[5]; // 1-100
+ Bit8u envlevel[4]; // 1-100
+ } MT32EMU_ALIGN_PACKED tvf;
+
+ struct tvaParam {
+ Bit8u level; // 0-100
+ Bit8u velosens; // 0-100
+ Bit8u biaspoint1; // 0-127 (<1A-<7C >1A-7C)
+ Bit8u biaslevel1; // 0-12 (-12 - 0)
+ Bit8u biaspoint2; // 0-127 (<1A-<7C >1A-7C)
+ Bit8u biaslevel2; // 0-12 (-12 - 0)
+ Bit8u envtkf; // TIME KEY FOLLOW 0-4
+ Bit8u envvkf; // VELOS KEY FOLL0W 0-4
+ Bit8u envtime[5]; // 1-100
+ Bit8u envlevel[4]; // 1-100
+ } MT32EMU_ALIGN_PACKED tva;
+ } MT32EMU_ALIGN_PACKED partial[4];
+} MT32EMU_ALIGN_PACKED;
+
+struct PatchParam {
+ Bit8u timbreGroup; // TIMBRE GROUP 0-3 (group A, group B, Memory, Rhythm)
+ Bit8u timbreNum; // TIMBRE NUMBER 0-63
+ Bit8u keyShift; // KEY SHIFT 0-48 (-24 - +24 semitones)
+ Bit8u fineTune; // FINE TUNE 0-100 (-50 - +50 cents)
+ Bit8u benderRange; // BENDER RANGE 0-24
+ Bit8u assignMode; // ASSIGN MODE 0-3 (POLY1, POLY2, POLY3, POLY4)
+ Bit8u reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON)
+ Bit8u dummy; // (DUMMY)
+} MT32EMU_ALIGN_PACKED;
+
+struct MemParams {
+ // NOTE: The MT-32 documentation only specifies PatchTemp areas for parts 1-8.
+ // The LAPC-I documentation specified an additional area for rhythm at the end,
+ // where all parameters but fine tune, assign mode and output level are ignored
+ struct PatchTemp {
+ PatchParam patch;
+ Bit8u outlevel; // OUTPUT LEVEL 0-100
+ Bit8u panpot; // PANPOT 0-14 (R-L)
+ Bit8u dummyv[6];
+ } MT32EMU_ALIGN_PACKED;
+
+ PatchTemp patchSettings[9];
+
+ struct RhythmTemp {
+ Bit8u timbre; // TIMBRE 0-94 (M1-M64,R1-30,OFF)
+ Bit8u outlevel; // OUTPUT LEVEL 0-100
+ Bit8u panpot; // PANPOT 0-14 (R-L)
+ Bit8u reverbSwitch; // REVERB SWITCH 0-1 (OFF,ON)
+ } MT32EMU_ALIGN_PACKED;
+
+ RhythmTemp rhythmSettings[85];
+
+ TimbreParam timbreSettings[8];
+
+ PatchParam patches[128];
+
+ // NOTE: There are only 30 timbres in the "rhythm" bank for MT-32; the additional 34 are for LAPC-I and above
+ struct PaddedTimbre {
+ TimbreParam timbre;
+ Bit8u padding[10];
+ } MT32EMU_ALIGN_PACKED;
+
+ PaddedTimbre timbres[64 + 64 + 64 + 64]; // Group A, Group B, Memory, Rhythm
+
+ struct SystemArea {
+ Bit8u masterTune; // MASTER TUNE 0-127 432.1-457.6Hz
+ Bit8u reverbMode; // REVERB MODE 0-3 (room, hall, plate, tap delay)
+ Bit8u reverbTime; // REVERB TIME 0-7 (1-8)
+ Bit8u reverbLevel; // REVERB LEVEL 0-7 (1-8)
+ Bit8u reserveSettings[9]; // PARTIAL RESERVE (PART 1) 0-32
+ Bit8u chanAssign[9]; // MIDI CHANNEL (PART1) 0-16 (1-16,OFF)
+ Bit8u masterVol; // MASTER VOLUME 0-100
+ } MT32EMU_ALIGN_PACKED;
+
+ SystemArea system;
+};
+
+#if defined(_MSC_VER) || defined (__MINGW32__)
+#pragma pack(pop)
+#else
+#pragma pack()
+#endif
+
+struct PCMWaveEntry {
+ Bit32u addr;
+ Bit32u len;
+ double tune;
+ bool loop;
+};
+
+struct soundaddr {
+ Bit16u pcmplace;
+ Bit16u pcmoffset;
+};
+
+struct StereoVolume {
+ Bit16s leftvol;
+ Bit16s rightvol;
+};
+
+// This is basically a per-partial, pre-processed combination of timbre and patch/rhythm settings
+struct PatchCache {
+ bool playPartial;
+ bool PCMPartial;
+ int pcm;
+ char waveform;
+ int pulsewidth;
+ int pwsens;
+
+ float pitch;
+
+ int lfodepth;
+ int lforate;
+ Bit32u lfoperiod;
+ int modsense;
+
+ float pitchKeyfollow;
+
+ int filtkeyfollow;
+
+ int tvfbias;
+ int tvfblevel;
+ int tvfdir;
+
+ int ampbias[2];
+ int ampblevel[2];
+ int ampdir[2];
+
+ int ampdepth;
+ int amplevel;
+
+ bool useBender;
+ float benderRange; // 0.0, 1.0, .., 24.0 (semitones)
+
+ TimbreParam::partialParam::envParam pitchEnv;
+ TimbreParam::partialParam::tvaParam ampEnv;
+ TimbreParam::partialParam::tvfParam filtEnv;
+
+ Bit32s pitchsustain;
+ Bit32s filtsustain;
+
+ Bit32u structureMix;
+ int structurePosition;
+ int structurePair;
+
+ // The following fields are actually common to all partials in the timbre
+ bool dirty;
+ Bit32u partialCount;
+ bool sustain;
+ float pitchShift;
+ bool reverb;
+ const StereoVolume *pansetptr;
+};
+
+class Partial; // Forward reference for class defined in partial.h
+
+struct dpoly {
+ bool isPlaying;
+
+ unsigned int key;
+ int freqnum;
+ int vel;
+
+ bool isDecay;
+
+ const Bit32u *volumeptr;
+
+ Partial *partials[4];
+
+ bool pedalhold; // This marks keys that have been released on the keyboard, but are being held by the pedal
+ bool sustain;
+
+ bool isActive() const;
+ Bit32u getAge() const;
+};
+
+}
+
+#endif
diff --git a/engines/sci/sfx/softseq/mt32/synth.cpp b/engines/sci/sfx/softseq/mt32/synth.cpp
new file mode 100644
index 0000000000..5ae196dc63
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/synth.cpp
@@ -0,0 +1,1199 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "mt32emu.h"
+
+#ifdef MACOSX
+// Older versions of Mac OS X didn't supply a powf function. To ensure
+// binary compatibility, we force using pow instead of powf (the only
+// potential drawback is that it might be a little bit slower).
+#define powf pow
+#endif
+
+namespace MT32Emu {
+
+const int MAX_SYSEX_SIZE = 512;
+
+const ControlROMMap ControlROMMaps[5] = {
+ // ID IDc IDbytes PCMmap PCMc tmbrA tmbrAO, tmbrB tmbrBO, tmbrR trC rhythm rhyC rsrv panpot prog
+ {0x4014, 22, "\000 ver1.04 14 July 87 ", 0x3000, 128, 0x8000, 0x0000, 0xC000, 0x4000, 0x3200, 30, 0x73A6, 85, 0x57C7, 0x57D0, 0x57E2}, // MT-32 revision 0
+ {0x4014, 22, "\000 ver1.06 31 Aug, 87 ", 0x3000, 128, 0x8000, 0x0000, 0xC000, 0x4000, 0x3200, 30, 0x7414, 85, 0x57D9, 0x57E2, 0x57F4}, // MT-32 revision 0
+ {0x4010, 22, "\000 ver1.07 10 Oct, 87 ", 0x3000, 128, 0x8000, 0x0000, 0xC000, 0x4000, 0x3200, 30, 0x73fe, 85, 0x57B1, 0x57BA, 0x57CC}, // MT-32 revision 1
+ {0x4010, 22, "\000verX.XX 30 Sep, 88 ", 0x3000, 128, 0x8000, 0x0000, 0xC000, 0x4000, 0x3200, 30, 0x741C, 85, 0x57E5, 0x57EE, 0x5800}, // MT-32 Blue Ridge mod
+ {0x2205, 22, "\000CM32/LAPC1.02 891205", 0x8100, 256, 0x8000, 0x8000, 0x8080, 0x8000, 0x8500, 64, 0x8580, 85, 0x4F93, 0x4F9C, 0x4FAE} // CM-32L
+ // (Note that all but CM-32L ROM actually have 86 entries for rhythmTemp)
+};
+
+float iir_filter_normal(float input, float *hist1_ptr, float *coef_ptr) {
+ float *hist2_ptr;
+ float output,new_hist;
+
+ hist2_ptr = hist1_ptr + 1; // next history
+
+ // 1st number of coefficients array is overall input scale factor, or filter gain
+ output = input * (*coef_ptr++);
+
+ output = output - *hist1_ptr * (*coef_ptr++);
+ new_hist = output - *hist2_ptr * (*coef_ptr++); // poles
+
+ output = new_hist + *hist1_ptr * (*coef_ptr++);
+ output = output + *hist2_ptr * (*coef_ptr++); // zeros
+
+ *hist2_ptr++ = *hist1_ptr;
+ *hist1_ptr++ = new_hist;
+ hist1_ptr++;
+ hist2_ptr++;
+
+ // i = 1
+ output = output - *hist1_ptr * (*coef_ptr++);
+ new_hist = output - *hist2_ptr * (*coef_ptr++); // poles
+
+ output = new_hist + *hist1_ptr * (*coef_ptr++);
+ output = output + *hist2_ptr * (*coef_ptr++); // zeros
+
+ *hist2_ptr++ = *hist1_ptr;
+ *hist1_ptr++ = new_hist;
+
+ return(output);
+}
+
+Bit8u Synth::calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum) {
+ for (unsigned int i = 0; i < len; i++) {
+ checksum = checksum + data[i];
+ }
+ checksum = checksum & 0x7f;
+ if (checksum)
+ checksum = 0x80 - checksum;
+ return checksum;
+}
+
+Synth::Synth() {
+ isOpen = false;
+ reverbModel = NULL;
+ partialManager = NULL;
+ memset(parts, 0, sizeof(parts));
+}
+
+Synth::~Synth() {
+ close(); // Make sure we're closed and everything is freed
+}
+
+int Synth::report(ReportType type, const void *data) {
+ if (myProp.report != NULL) {
+ return myProp.report(myProp.userData, type, data);
+ }
+ return 0;
+}
+
+void Synth::printDebug(const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ if (myProp.printDebug != NULL) {
+ myProp.printDebug(myProp.userData, fmt, ap);
+ } else {
+ vprintf(fmt, ap);
+ printf("\n");
+ }
+ va_end(ap);
+}
+
+void Synth::initReverb(Bit8u newRevMode, Bit8u newRevTime, Bit8u newRevLevel) {
+ // FIXME:KG: I don't think it's necessary to recreate the reverbModel... Just set the parameters
+ if (reverbModel != NULL)
+ delete reverbModel;
+ reverbModel = new revmodel();
+
+ switch (newRevMode) {
+ case 0:
+ reverbModel->setroomsize(.1f);
+ reverbModel->setdamp(.75f);
+ break;
+ case 1:
+ reverbModel->setroomsize(.5f);
+ reverbModel->setdamp(.5f);
+ break;
+ case 2:
+ reverbModel->setroomsize(.5f);
+ reverbModel->setdamp(.1f);
+ break;
+ case 3:
+ reverbModel->setroomsize(1.0f);
+ reverbModel->setdamp(.75f);
+ break;
+ default:
+ reverbModel->setroomsize(.1f);
+ reverbModel->setdamp(.5f);
+ break;
+ }
+ reverbModel->setdry(1);
+ reverbModel->setwet((float)newRevLevel / 8.0f);
+ reverbModel->setwidth((float)newRevTime / 8.0f);
+}
+
+File *Synth::openFile(const char *filename, File::OpenMode mode) {
+ if (myProp.openFile != NULL) {
+ return myProp.openFile(myProp.userData, filename, mode);
+ }
+ char pathBuf[2048];
+ if (myProp.baseDir != NULL) {
+ strcpy(&pathBuf[0], myProp.baseDir);
+ strcat(&pathBuf[0], filename);
+ filename = pathBuf;
+ }
+ ANSIFile *file = new ANSIFile();
+ if (!file->open(filename, mode)) {
+ delete file;
+ return NULL;
+ }
+ return file;
+}
+
+void Synth::closeFile(File *file) {
+ if (myProp.closeFile != NULL) {
+ myProp.closeFile(myProp.userData, file);
+ } else {
+ file->close();
+ delete file;
+ }
+}
+
+bool Synth::loadPreset(File *file) {
+ bool inSys = false;
+ Bit8u sysexBuf[MAX_SYSEX_SIZE];
+ Bit16u syslen = 0;
+ bool rc = true;
+ for (;;) {
+ Bit8u c;
+ if (!file->readBit8u(&c)) {
+ if (!file->isEOF()) {
+ rc = false;
+ }
+ break;
+ }
+ sysexBuf[syslen] = c;
+ if (inSys) {
+ syslen++;
+ if (c == 0xF7) {
+ playSysex(&sysexBuf[0], syslen);
+ inSys = false;
+ syslen = 0;
+ } else if (syslen == MAX_SYSEX_SIZE) {
+ printDebug("MAX_SYSEX_SIZE (%d) exceeded while processing preset, ignoring message", MAX_SYSEX_SIZE);
+ inSys = false;
+ syslen = 0;
+ }
+ } else if (c == 0xF0) {
+ syslen++;
+ inSys = true;
+ }
+ }
+ return rc;
+}
+
+bool Synth::loadControlROM(const char *filename) {
+ File *file = openFile(filename, File::OpenMode_read); // ROM File
+ if (file == NULL) {
+ return false;
+ }
+ bool rc = (file->read(controlROMData, CONTROL_ROM_SIZE) == CONTROL_ROM_SIZE);
+
+ closeFile(file);
+ if (!rc)
+ return rc;
+
+ // Control ROM successfully loaded, now check whether it's a known type
+ controlROMMap = NULL;
+ for (unsigned int i = 0; i < sizeof (ControlROMMaps) / sizeof (ControlROMMaps[0]); i++) {
+ if (memcmp(&controlROMData[ControlROMMaps[i].idPos], ControlROMMaps[i].idBytes, ControlROMMaps[i].idLen) == 0) {
+ controlROMMap = &ControlROMMaps[i];
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Synth::loadPCMROM(const char *filename) {
+ File *file = openFile(filename, File::OpenMode_read); // ROM File
+ if (file == NULL) {
+ return false;
+ }
+ bool rc = true;
+ int i;
+ for (i = 0; i < pcmROMSize; i++) {
+ Bit8u s;
+ if (!file->readBit8u(&s)) {
+ if (!file->isEOF()) {
+ rc = false;
+ }
+ break;
+ }
+ Bit8u c;
+ if (!file->readBit8u(&c)) {
+ if (!file->isEOF()) {
+ rc = false;
+ } else {
+ printDebug("PCM ROM file has an odd number of bytes! Ignoring last");
+ }
+ break;
+ }
+
+ short e;
+ int bit;
+ int u;
+ int order[16] = {0, 9, 1 ,2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 8};
+
+ e = 0;
+ for (u = 0; u < 15; u++) {
+ if (order[u] < 8)
+ bit = (s >> (7 - order[u])) & 0x1;
+ else
+ bit = (c >> (7 - (order[u] - 8))) & 0x1;
+ e = e | (short)(bit << (15 - u));
+ }
+
+ /*
+ //Bit16s e = ( ((s & 0x7f) << 4) | ((c & 0x40) << 6) | ((s & 0x80) << 6) | ((c & 0x3f))) << 2;
+ if (e<0)
+ e = -32767 - e;
+ int ut = abs(e);
+ int dif = 0x7fff - ut;
+ x = exp(((float)((float)0x8000-(float)dif) / (float)0x1000));
+ e = (int)((float)e * (x/3200));
+ */
+
+ // File is companded (dB?), convert to linear PCM
+ // MINDB = -96
+ // MAXDB = -15
+ float testval;
+ testval = (float)((~e) & 0x7fff);
+ testval = -(testval / 400.00f);
+ //testval = -(testval / 341.32291666666666666666666666667);
+ float vol = powf(8, testval / 20) * 32767.0f;
+
+ if (e > 0)
+ vol = -vol;
+
+ pcmROMData[i] = (Bit16s)vol;
+ }
+ if (i != pcmROMSize) {
+ printDebug("PCM ROM file is too short (expected %d, got %d)", pcmROMSize, i);
+ rc = false;
+ }
+ closeFile(file);
+ return rc;
+}
+
+bool Synth::initPCMList(Bit16u mapAddress, Bit16u count) {
+ ControlROMPCMStruct *tps = (ControlROMPCMStruct *)&controlROMData[mapAddress];
+ for (int i = 0; i < count; i++) {
+ int rAddr = tps[i].pos * 0x800;
+ int rLenExp = (tps[i].len & 0x70) >> 4;
+ int rLen = 0x800 << rLenExp;
+ bool rLoop = (tps[i].len & 0x80) != 0;
+ //Bit8u rFlag = tps[i].len & 0x0F;
+ Bit16u rTuneOffset = (tps[i].pitchMSB << 8) | tps[i].pitchLSB;
+ // The number below is confirmed to a reasonable degree of accuracy on CM-32L
+ double STANDARDFREQ = 442.0;
+ float rTune = (float)(STANDARDFREQ * pow(2.0, (0x5000 - rTuneOffset) / 4056.0 - 9.0 / 12.0));
+ //printDebug("%f,%d,%d", pTune, tps[i].pitchCoarse, tps[i].pitchFine);
+ if (rAddr + rLen > pcmROMSize) {
+ printDebug("Control ROM error: Wave map entry %d points to invalid PCM address 0x%04X, length 0x%04X", i, rAddr, rLen);
+ return false;
+ }
+ pcmWaves[i].addr = rAddr;
+ pcmWaves[i].len = rLen;
+ pcmWaves[i].loop = rLoop;
+ pcmWaves[i].tune = rTune;
+ }
+ return false;
+}
+
+bool Synth::initRhythmTimbre(int timbreNum, const Bit8u *mem, unsigned int memLen) {
+ if (memLen < sizeof(TimbreParam::commonParam)) {
+ return false;
+ }
+ TimbreParam *timbre = &mt32ram.timbres[timbreNum].timbre;
+ memcpy(&timbre->common, mem, 14);
+ unsigned int memPos = 14;
+ char drumname[11];
+ strncpy(drumname, timbre->common.name, 10);
+ drumname[10] = 0;
+ for (int t = 0; t < 4; t++) {
+ if (((timbre->common.pmute >> t) & 0x1) == 0x1) {
+ if (memPos + 58 >= memLen) {
+ return false;
+ }
+ memcpy(&timbre->partial[t], mem + memPos, 58);
+ memPos += 58;
+ }
+ }
+ return true;
+}
+
+bool Synth::initRhythmTimbres(Bit16u mapAddress, Bit16u count) {
+ const Bit8u *drumMap = &controlROMData[mapAddress];
+ int timbreNum = 192;
+ for (Bit16u i = 0; i < count * 2; i += 2) {
+ Bit16u address = (drumMap[i + 1] << 8) | drumMap[i];
+ /*
+ // This check is nonsensical when the control ROM is the full 64KB addressable by 16-bit absolute pointers (which it is)
+ if (address >= CONTROL_ROM_SIZE) {
+ printDebug("Control ROM error: Timbre map entry 0x%04x points to invalid timbre address 0x%04x", i, address);
+ return false;
+ }
+ */
+ if (!initRhythmTimbre(timbreNum++, &controlROMData[address], CONTROL_ROM_SIZE - address)) {
+ printDebug("Control ROM error: Timbre map entry 0x%04x points to invalid timbre 0x%04x", i, address);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool Synth::initTimbres(Bit16u mapAddress, Bit16u offset, int startTimbre) {
+ for (Bit16u i = mapAddress; i < mapAddress + 0x80; i += 2) {
+ Bit16u address = (controlROMData[i + 1] << 8) | controlROMData[i];
+ if (address + sizeof(TimbreParam) > CONTROL_ROM_SIZE) {
+ printDebug("Control ROM error: Timbre map entry 0x%04x points to invalid timbre address 0x%04x", i, address);
+ return false;
+ }
+ address = address + offset;
+ TimbreParam *timbre = &mt32ram.timbres[startTimbre++].timbre;
+ memcpy(timbre, &controlROMData[address], sizeof(TimbreParam));
+ }
+ return true;
+}
+
+bool Synth::open(SynthProperties &useProp) {
+ if (isOpen)
+ return false;
+
+ myProp = useProp;
+ if (useProp.baseDir != NULL) {
+ myProp.baseDir = new char[strlen(useProp.baseDir) + 1];
+ strcpy(myProp.baseDir, useProp.baseDir);
+ }
+
+ // This is to help detect bugs
+ memset(&mt32ram, '?', sizeof(mt32ram));
+
+ printDebug("Loading Control ROM");
+ if (!loadControlROM("CM32L_CONTROL.ROM")) {
+ if (!loadControlROM("MT32_CONTROL.ROM")) {
+ printDebug("Init Error - Missing or invalid MT32_CONTROL.ROM");
+ report(ReportType_errorControlROM, NULL);
+ return false;
+ }
+ }
+
+ // 512KB PCM ROM for MT-32, etc.
+ // 1MB PCM ROM for CM-32L, LAPC-I, CM-64, CM-500
+ // Note that the size below is given in samples (16-bit), not bytes
+ pcmROMSize = controlROMMap->pcmCount == 256 ? 512 * 1024 : 256 * 1024;
+ pcmROMData = new Bit16s[pcmROMSize];
+
+ printDebug("Loading PCM ROM");
+ if (!loadPCMROM("CM32L_PCM.ROM")) {
+ if (!loadPCMROM("MT32_PCM.ROM")) {
+ printDebug("Init Error - Missing MT32_PCM.ROM");
+ report(ReportType_errorPCMROM, NULL);
+ return false;
+ }
+ }
+
+ printDebug("Initialising Timbre Bank A");
+ if (!initTimbres(controlROMMap->timbreAMap, controlROMMap->timbreAOffset, 0)) {
+ return false;
+ }
+
+ printDebug("Initialising Timbre Bank B");
+ if (!initTimbres(controlROMMap->timbreBMap, controlROMMap->timbreBOffset, 64)) {
+ return false;
+ }
+
+ printDebug("Initialising Timbre Bank R");
+ if (!initRhythmTimbres(controlROMMap->timbreRMap, controlROMMap->timbreRCount)) {
+ return false;
+ }
+
+ printDebug("Initialising Timbre Bank M");
+ // CM-64 seems to initialise all bytes in this bank to 0.
+ memset(&mt32ram.timbres[128], 0, sizeof (mt32ram.timbres[128]) * 64);
+
+ partialManager = new PartialManager(this);
+
+ pcmWaves = new PCMWaveEntry[controlROMMap->pcmCount];
+
+ printDebug("Initialising PCM List");
+ initPCMList(controlROMMap->pcmTable, controlROMMap->pcmCount);
+
+ printDebug("Initialising Rhythm Temp");
+ memcpy(mt32ram.rhythmSettings, &controlROMData[controlROMMap->rhythmSettings], controlROMMap->rhythmSettingsCount * 4);
+
+ printDebug("Initialising Patches");
+ for (Bit8u i = 0; i < 128; i++) {
+ PatchParam *patch = &mt32ram.patches[i];
+ patch->timbreGroup = i / 64;
+ patch->timbreNum = i % 64;
+ patch->keyShift = 24;
+ patch->fineTune = 50;
+ patch->benderRange = 12;
+ patch->assignMode = 0;
+ patch->reverbSwitch = 1;
+ patch->dummy = 0;
+ }
+
+ printDebug("Initialising System");
+ // The MT-32 manual claims that "Standard pitch" is 442Hz.
+ mt32ram.system.masterTune = 0x4A; // Confirmed on CM-64
+ mt32ram.system.reverbMode = 0; // Confirmed
+ mt32ram.system.reverbTime = 5; // Confirmed
+ mt32ram.system.reverbLevel = 3; // Confirmed
+ memcpy(mt32ram.system.reserveSettings, &controlROMData[controlROMMap->reserveSettings], 9); // Confirmed
+ for (Bit8u i = 0; i < 9; i++) {
+ // This is the default: {1, 2, 3, 4, 5, 6, 7, 8, 9}
+ // An alternative configuration can be selected by holding "Master Volume"
+ // and pressing "PART button 1" on the real MT-32's frontpanel.
+ // The channel assignment is then {0, 1, 2, 3, 4, 5, 6, 7, 9}
+ mt32ram.system.chanAssign[i] = i + 1;
+ }
+ mt32ram.system.masterVol = 100; // Confirmed
+ if (!refreshSystem())
+ return false;
+
+ for (int i = 0; i < 8; i++) {
+ mt32ram.patchSettings[i].outlevel = 80;
+ mt32ram.patchSettings[i].panpot = controlROMData[controlROMMap->panSettings + i];
+ memset(mt32ram.patchSettings[i].dummyv, 0, sizeof(mt32ram.patchSettings[i].dummyv));
+ parts[i] = new Part(this, i);
+ parts[i]->setProgram(controlROMData[controlROMMap->programSettings + i]);
+ }
+ parts[8] = new RhythmPart(this, 8);
+
+ // For resetting mt32 mid-execution
+ mt32default = mt32ram;
+
+ iirFilter = &iir_filter_normal;
+
+#ifdef MT32EMU_HAVE_X86
+ bool availableSSE = DetectSIMD();
+ bool available3DNow = Detect3DNow();
+
+ if (availableSSE)
+ report(ReportType_availableSSE, NULL);
+ if (available3DNow)
+ report(ReportType_available3DNow, NULL);
+
+ if (available3DNow) {
+ printDebug("Detected and using SIMD (AMD 3DNow) extensions");
+ iirFilter = &iir_filter_3dnow;
+ report(ReportType_using3DNow, NULL);
+ } else if (availableSSE) {
+ printDebug("Detected and using SIMD (Intel SSE) extensions");
+ iirFilter = &iir_filter_sse;
+ report(ReportType_usingSSE, NULL);
+ }
+#endif
+
+ isOpen = true;
+ isEnabled = false;
+
+ printDebug("*** Initialisation complete ***");
+ return true;
+}
+
+void Synth::close(void) {
+ if (!isOpen)
+ return;
+
+ tables.freeNotes();
+ if (partialManager != NULL) {
+ delete partialManager;
+ partialManager = NULL;
+ }
+
+ if (reverbModel != NULL) {
+ delete reverbModel;
+ reverbModel = NULL;
+ }
+
+ for (int i = 0; i < 9; i++) {
+ if (parts[i] != NULL) {
+ delete parts[i];
+ parts[i] = NULL;
+ }
+ }
+ if (myProp.baseDir != NULL) {
+ delete myProp.baseDir;
+ myProp.baseDir = NULL;
+ }
+
+ delete[] pcmWaves;
+ delete[] pcmROMData;
+ isOpen = false;
+}
+
+void Synth::playMsg(Bit32u msg) {
+ // FIXME: Implement active sensing
+ unsigned char code = (unsigned char)((msg & 0x0000F0) >> 4);
+ unsigned char chan = (unsigned char) (msg & 0x00000F);
+ unsigned char note = (unsigned char)((msg & 0x00FF00) >> 8);
+ unsigned char velocity = (unsigned char)((msg & 0xFF0000) >> 16);
+ isEnabled = true;
+
+ //printDebug("Playing chan %d, code 0x%01x note: 0x%02x", chan, code, note);
+
+ char part = chantable[chan];
+ if (part < 0 || part > 8) {
+ printDebug("Play msg on unreg chan %d (%d): code=0x%01x, vel=%d", chan, part, code, velocity);
+ return;
+ }
+ playMsgOnPart(part, code, note, velocity);
+}
+
+void Synth::playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity) {
+ Bit32u bend;
+
+ //printDebug("Synth::playMsg(0x%02x)",msg);
+ switch (code) {
+ case 0x8:
+ //printDebug("Note OFF - Part %d", part);
+ // The MT-32 ignores velocity for note off
+ parts[part]->stopNote(note);
+ break;
+ case 0x9:
+ //printDebug("Note ON - Part %d, Note %d Vel %d", part, note, velocity);
+ if (velocity == 0) {
+ // MIDI defines note-on with velocity 0 as being the same as note-off with velocity 40
+ parts[part]->stopNote(note);
+ } else {
+ parts[part]->playNote(note, velocity);
+ }
+ break;
+ case 0xB: // Control change
+ switch (note) {
+ case 0x01: // Modulation
+ //printDebug("Modulation: %d", velocity);
+ parts[part]->setModulation(velocity);
+ break;
+ case 0x07: // Set volume
+ //printDebug("Volume set: %d", velocity);
+ parts[part]->setVolume(velocity);
+ break;
+ case 0x0A: // Pan
+ //printDebug("Pan set: %d", velocity);
+ parts[part]->setPan(velocity);
+ break;
+ case 0x0B:
+ //printDebug("Expression set: %d", velocity);
+ parts[part]->setExpression(velocity);
+ break;
+ case 0x40: // Hold (sustain) pedal
+ //printDebug("Hold pedal set: %d", velocity);
+ parts[part]->setHoldPedal(velocity>=64);
+ break;
+
+ case 0x79: // Reset all controllers
+ //printDebug("Reset all controllers");
+ //FIXME: Check for accuracy against real thing
+ parts[part]->setVolume(100);
+ parts[part]->setExpression(127);
+ parts[part]->setPan(64);
+ parts[part]->setBend(0x2000);
+ parts[part]->setHoldPedal(false);
+ break;
+
+ case 0x7B: // All notes off
+ //printDebug("All notes off");
+ parts[part]->allNotesOff();
+ break;
+
+ default:
+ printDebug("Unknown MIDI Control code: 0x%02x - vel 0x%02x", note, velocity);
+ break;
+ }
+
+ break;
+ case 0xC: // Program change
+ //printDebug("Program change %01x", note);
+ parts[part]->setProgram(note);
+ break;
+ case 0xE: // Pitch bender
+ bend = (velocity << 7) | (note);
+ //printDebug("Pitch bender %02x", bend);
+ parts[part]->setBend(bend);
+ break;
+ default:
+ printDebug("Unknown Midi code: 0x%01x - %02x - %02x", code, note, velocity);
+ break;
+ }
+
+ //midiOutShortMsg(m_out, msg);
+}
+
+void Synth::playSysex(const Bit8u *sysex, Bit32u len) {
+ if (len < 2) {
+ printDebug("playSysex: Message is too short for sysex (%d bytes)", len);
+ }
+ if (sysex[0] != 0xF0) {
+ printDebug("playSysex: Message lacks start-of-sysex (0xF0)");
+ return;
+ }
+ // Due to some programs (e.g. Java) sending buffers with junk at the end, we have to go through and find the end marker rather than relying on len.
+ Bit32u endPos;
+ for (endPos = 1; endPos < len; endPos++)
+ {
+ if (sysex[endPos] == 0xF7)
+ break;
+ }
+ if (endPos == len) {
+ printDebug("playSysex: Message lacks end-of-sysex (0xf7)");
+ return;
+ }
+ playSysexWithoutFraming(sysex + 1, endPos - 1);
+}
+
+void Synth::playSysexWithoutFraming(const Bit8u *sysex, Bit32u len) {
+ if (len < 4) {
+ printDebug("playSysexWithoutFraming: Message is too short (%d bytes)!", len);
+ return;
+ }
+ if (sysex[0] != SYSEX_MANUFACTURER_ROLAND) {
+ printDebug("playSysexWithoutFraming: Header not intended for this device manufacturer: %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]);
+ return;
+ }
+ if (sysex[2] == SYSEX_MDL_D50) {
+ printDebug("playSysexWithoutFraming: Header is intended for model D-50 (not yet supported): %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]);
+ return;
+ }
+ else if (sysex[2] != SYSEX_MDL_MT32) {
+ printDebug("playSysexWithoutFraming: Header not intended for model MT-32: %02x %02x %02x %02x", (int)sysex[0], (int)sysex[1], (int)sysex[2], (int)sysex[3]);
+ return;
+ }
+ playSysexWithoutHeader(sysex[1], sysex[3], sysex + 4, len - 4);
+}
+
+void Synth::playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len) {
+ if (device > 0x10) {
+ // We have device ID 0x10 (default, but changeable, on real MT-32), < 0x10 is for channels
+ printDebug("playSysexWithoutHeader: Message is not intended for this device ID (provided: %02x, expected: 0x10 or channel)", (int)device);
+ return;
+ }
+ if (len < 4) {
+ printDebug("playSysexWithoutHeader: Message is too short (%d bytes)!", len);
+ return;
+ }
+ unsigned char checksum = calcSysexChecksum(sysex, len - 1, 0);
+ if (checksum != sysex[len - 1]) {
+ printDebug("playSysexWithoutHeader: Message checksum is incorrect (provided: %02x, expected: %02x)!", sysex[len - 1], checksum);
+ return;
+ }
+ len -= 1; // Exclude checksum
+ switch (command) {
+ case SYSEX_CMD_DT1:
+ writeSysex(device, sysex, len);
+ break;
+ case SYSEX_CMD_RQ1:
+ readSysex(device, sysex, len);
+ break;
+ default:
+ printDebug("playSysexWithoutFraming: Unsupported command %02x", command);
+ return;
+ }
+}
+
+void Synth::readSysex(unsigned char /*device*/, const Bit8u * /*sysex*/, Bit32u /*len*/) {
+}
+
+const MemoryRegion memoryRegions[8] = {
+ {MR_PatchTemp, MT32EMU_MEMADDR(0x030000), sizeof(MemParams::PatchTemp), 9},
+ {MR_RhythmTemp, MT32EMU_MEMADDR(0x030110), sizeof(MemParams::RhythmTemp), 85},
+ {MR_TimbreTemp, MT32EMU_MEMADDR(0x040000), sizeof(TimbreParam), 8},
+ {MR_Patches, MT32EMU_MEMADDR(0x050000), sizeof(PatchParam), 128},
+ {MR_Timbres, MT32EMU_MEMADDR(0x080000), sizeof(MemParams::PaddedTimbre), 64 + 64 + 64 + 64},
+ {MR_System, MT32EMU_MEMADDR(0x100000), sizeof(MemParams::SystemArea), 1},
+ {MR_Display, MT32EMU_MEMADDR(0x200000), MAX_SYSEX_SIZE - 1, 1},
+ {MR_Reset, MT32EMU_MEMADDR(0x7F0000), 0x3FFF, 1}
+};
+
+const int NUM_REGIONS = sizeof(memoryRegions) / sizeof(MemoryRegion);
+
+void Synth::writeSysex(unsigned char device, const Bit8u *sysex, Bit32u len) {
+ Bit32u addr = (sysex[0] << 16) | (sysex[1] << 8) | (sysex[2]);
+ addr = MT32EMU_MEMADDR(addr);
+ sysex += 3;
+ len -= 3;
+ //printDebug("Sysex addr: 0x%06x", MT32EMU_SYSEXMEMADDR(addr));
+ // NOTE: Please keep both lower and upper bounds in each check, for ease of reading
+
+ // Process channel-specific sysex by converting it to device-global
+ if (device < 0x10) {
+ printDebug("WRITE-CHANNEL: Channel %d temp area 0x%06x", device, MT32EMU_SYSEXMEMADDR(addr));
+ if (/*addr >= MT32EMU_MEMADDR(0x000000) && */addr < MT32EMU_MEMADDR(0x010000)) {
+ int offset;
+ if (chantable[device] == -1) {
+ printDebug(" (Channel not mapped to a partial... 0 offset)");
+ offset = 0;
+ } else if (chantable[device] == 8) {
+ printDebug(" (Channel mapped to rhythm... 0 offset)");
+ offset = 0;
+ } else {
+ offset = chantable[device] * sizeof(MemParams::PatchTemp);
+ printDebug(" (Setting extra offset to %d)", offset);
+ }
+ addr += MT32EMU_MEMADDR(0x030000) + offset;
+ } else if (/*addr >= 0x010000 && */ addr < MT32EMU_MEMADDR(0x020000)) {
+ addr += MT32EMU_MEMADDR(0x030110) - MT32EMU_MEMADDR(0x010000);
+ } else if (/*addr >= 0x020000 && */ addr < MT32EMU_MEMADDR(0x030000)) {
+ int offset;
+ if (chantable[device] == -1) {
+ printDebug(" (Channel not mapped to a partial... 0 offset)");
+ offset = 0;
+ } else if (chantable[device] == 8) {
+ printDebug(" (Channel mapped to rhythm... 0 offset)");
+ offset = 0;
+ } else {
+ offset = chantable[device] * sizeof(TimbreParam);
+ printDebug(" (Setting extra offset to %d)", offset);
+ }
+ addr += MT32EMU_MEMADDR(0x040000) - MT32EMU_MEMADDR(0x020000) + offset;
+ } else {
+ printDebug("PlaySysexWithoutHeader: Invalid channel %d address 0x%06x", device, MT32EMU_SYSEXMEMADDR(addr));
+ return;
+ }
+ }
+
+ // Process device-global sysex (possibly converted from channel-specific sysex above)
+ for (;;) {
+ // Find the appropriate memory region
+ int regionNum;
+ const MemoryRegion *region = NULL; // Initialised to please compiler
+ for (regionNum = 0; regionNum < NUM_REGIONS; regionNum++) {
+ region = &memoryRegions[regionNum];
+ if (region->contains(addr)) {
+ writeMemoryRegion(region, addr, region->getClampedLen(addr, len), sysex);
+ break;
+ }
+ }
+ if (regionNum == NUM_REGIONS) {
+ printDebug("Sysex write to unrecognised address %06x, len %d", MT32EMU_SYSEXMEMADDR(addr), len);
+ break;
+ }
+ Bit32u next = region->next(addr, len);
+ if (next == 0) {
+ break;
+ }
+ addr += next;
+ sysex += next;
+ len -= next;
+ }
+}
+
+void Synth::readMemory(Bit32u addr, Bit32u len, Bit8u *data) {
+ int regionNum;
+ const MemoryRegion *region = NULL;
+ for (regionNum = 0; regionNum < NUM_REGIONS; regionNum++) {
+ region = &memoryRegions[regionNum];
+ if (region->contains(addr)) {
+ readMemoryRegion(region, addr, len, data);
+ break;
+ }
+ }
+}
+
+void Synth::readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, Bit8u *data) {
+ unsigned int first = region->firstTouched(addr);
+ //unsigned int last = region->lastTouched(addr, len);
+ unsigned int off = region->firstTouchedOffset(addr);
+ len = region->getClampedLen(addr, len);
+
+ unsigned int m;
+
+ switch(region->type) {
+ case MR_PatchTemp:
+ for (m = 0; m < len; m++)
+ data[m] = ((Bit8u *)&mt32ram.patchSettings[first])[off + m];
+ break;
+ case MR_RhythmTemp:
+ for (m = 0; m < len; m++)
+ data[m] = ((Bit8u *)&mt32ram.rhythmSettings[first])[off + m];
+ break;
+ case MR_TimbreTemp:
+ for (m = 0; m < len; m++)
+ data[m] = ((Bit8u *)&mt32ram.timbreSettings[first])[off + m];
+ break;
+ case MR_Patches:
+ for (m = 0; m < len; m++)
+ data[m] = ((Bit8u *)&mt32ram.patches[first])[off + m];
+ break;
+ case MR_Timbres:
+ for (m = 0; m < len; m++)
+ data[m] = ((Bit8u *)&mt32ram.timbres[first])[off + m];
+ break;
+ case MR_System:
+ for (m = 0; m < len; m++)
+ data[m] = ((Bit8u *)&mt32ram.system)[m + off];
+ break;
+ default:
+ for (m = 0; m < len; m += 2) {
+ data[m] = 0xff;
+ if (m + 1 < len) {
+ data[m+1] = (Bit8u)region->type;
+ }
+ }
+ // TODO: Don't care about the others ATM
+ break;
+ }
+
+}
+
+void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, const Bit8u *data) {
+ unsigned int first = region->firstTouched(addr);
+ unsigned int last = region->lastTouched(addr, len);
+ unsigned int off = region->firstTouchedOffset(addr);
+ switch (region->type) {
+ case MR_PatchTemp:
+ for (unsigned int m = 0; m < len; m++) {
+ ((Bit8u *)&mt32ram.patchSettings[first])[off + m] = data[m];
+ }
+ //printDebug("Patch temp: Patch %d, offset %x, len %d", off/16, off % 16, len);
+
+ for (unsigned int i = first; i <= last; i++) {
+ int absTimbreNum = mt32ram.patchSettings[i].patch.timbreGroup * 64 + mt32ram.patchSettings[i].patch.timbreNum;
+ char timbreName[11];
+ memcpy(timbreName, mt32ram.timbres[absTimbreNum].timbre.common.name, 10);
+ timbreName[10] = 0;
+ printDebug("WRITE-PARTPATCH (%d-%d@%d..%d): %d; timbre=%d (%s), outlevel=%d", first, last, off, off + len, i, absTimbreNum, timbreName, mt32ram.patchSettings[i].outlevel);
+ if (parts[i] != NULL) {
+ if (i != 8) {
+ // Note: Confirmed on CM-64 that we definitely *should* update the timbre here,
+ // but only in the case that the sysex actually writes to those values
+ if (i == first && off > 2) {
+ printDebug(" (Not updating timbre, since those values weren't touched)");
+ } else {
+ parts[i]->setTimbre(&mt32ram.timbres[parts[i]->getAbsTimbreNum()].timbre);
+ }
+ }
+ parts[i]->refresh();
+ }
+ }
+ break;
+ case MR_RhythmTemp:
+ for (unsigned int m = 0; m < len; m++)
+ ((Bit8u *)&mt32ram.rhythmSettings[first])[off + m] = data[m];
+ for (unsigned int i = first; i <= last; i++) {
+ int timbreNum = mt32ram.rhythmSettings[i].timbre;
+ char timbreName[11];
+ if (timbreNum < 94) {
+ memcpy(timbreName, mt32ram.timbres[128 + timbreNum].timbre.common.name, 10);
+ timbreName[10] = 0;
+ } else {
+ strcpy(timbreName, "[None]");
+ }
+ printDebug("WRITE-RHYTHM (%d-%d@%d..%d): %d; level=%02x, panpot=%02x, reverb=%02x, timbre=%d (%s)", first, last, off, off + len, i, mt32ram.rhythmSettings[i].outlevel, mt32ram.rhythmSettings[i].panpot, mt32ram.rhythmSettings[i].reverbSwitch, mt32ram.rhythmSettings[i].timbre, timbreName);
+ }
+ if (parts[8] != NULL) {
+ parts[8]->refresh();
+ }
+ break;
+ case MR_TimbreTemp:
+ for (unsigned int m = 0; m < len; m++)
+ ((Bit8u *)&mt32ram.timbreSettings[first])[off + m] = data[m];
+ for (unsigned int i = first; i <= last; i++) {
+ char instrumentName[11];
+ memcpy(instrumentName, mt32ram.timbreSettings[i].common.name, 10);
+ instrumentName[10] = 0;
+ printDebug("WRITE-PARTTIMBRE (%d-%d@%d..%d): timbre=%d (%s)", first, last, off, off + len, i, instrumentName);
+ if (parts[i] != NULL) {
+ parts[i]->refresh();
+ }
+ }
+ break;
+ case MR_Patches:
+ for (unsigned int m = 0; m < len; m++)
+ ((Bit8u *)&mt32ram.patches[first])[off + m] = data[m];
+ for (unsigned int i = first; i <= last; i++) {
+ PatchParam *patch = &mt32ram.patches[i];
+ int patchAbsTimbreNum = patch->timbreGroup * 64 + patch->timbreNum;
+ char instrumentName[11];
+ memcpy(instrumentName, mt32ram.timbres[patchAbsTimbreNum].timbre.common.name, 10);
+ instrumentName[10] = 0;
+ Bit8u *n = (Bit8u *)patch;
+ printDebug("WRITE-PATCH (%d-%d@%d..%d): %d; timbre=%d (%s) %02X%02X%02X%02X%02X%02X%02X%02X", first, last, off, off + len, i, patchAbsTimbreNum, instrumentName, n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7]);
+ // FIXME:KG: The below is definitely dodgy. We just guess that this is the patch that the part was using
+ // based on a timbre match (but many patches could have the same timbre!)
+ // If this refresh is really correct, we should store the patch number in use by each part.
+ /*
+ for (int part = 0; part < 8; part++) {
+ if (parts[part] != NULL) {
+ int partPatchAbsTimbreNum = mt32ram.patchSettings[part].patch.timbreGroup * 64 + mt32ram.patchSettings[part].patch.timbreNum;
+ if (parts[part]->getAbsTimbreNum() == patchAbsTimbreNum) {
+ parts[part]->setPatch(patch);
+ parts[part]->RefreshPatch();
+ }
+ }
+ }
+ */
+ }
+ break;
+ case MR_Timbres:
+ // Timbres
+ first += 128;
+ last += 128;
+ for (unsigned int m = 0; m < len; m++)
+ ((Bit8u *)&mt32ram.timbres[first])[off + m] = data[m];
+ for (unsigned int i = first; i <= last; i++) {
+ char instrumentName[11];
+ memcpy(instrumentName, mt32ram.timbres[i].timbre.common.name, 10);
+ instrumentName[10] = 0;
+ printDebug("WRITE-TIMBRE (%d-%d@%d..%d): %d; name=\"%s\"", first, last, off, off + len, i, instrumentName);
+ // FIXME:KG: Not sure if the stuff below should be done (for rhythm and/or parts)...
+ // Does the real MT-32 automatically do this?
+ for (unsigned int part = 0; part < 9; part++) {
+ if (parts[part] != NULL) {
+ parts[part]->refreshTimbre(i);
+ }
+ }
+ }
+ break;
+ case MR_System:
+ for (unsigned int m = 0; m < len; m++)
+ ((Bit8u *)&mt32ram.system)[m + off] = data[m];
+
+ report(ReportType_devReconfig, NULL);
+
+ printDebug("WRITE-SYSTEM:");
+ refreshSystem();
+ break;
+ case MR_Display:
+ char buf[MAX_SYSEX_SIZE];
+ memcpy(&buf, &data[0], len);
+ buf[len] = 0;
+ printDebug("WRITE-LCD: %s", buf);
+ report(ReportType_lcdMessage, buf);
+ break;
+ case MR_Reset:
+ printDebug("RESET");
+ report(ReportType_devReset, NULL);
+ partialManager->deactivateAll();
+ mt32ram = mt32default;
+ for (int i = 0; i < 9; i++) {
+ parts[i]->refresh();
+ }
+ isEnabled = false;
+ break;
+ }
+}
+
+bool Synth::refreshSystem() {
+ memset(chantable, -1, sizeof(chantable));
+
+ for (unsigned int i = 0; i < 9; i++) {
+ //LOG(LOG_MISC|LOG_ERROR,"Part %d set to MIDI channel %d",i,mt32ram.system.chanAssign[i]);
+ if (mt32ram.system.chanAssign[i] == 16 && parts[i] != NULL) {
+ parts[i]->allSoundOff();
+ } else {
+ chantable[(int)mt32ram.system.chanAssign[i]] = (char)i;
+ }
+ }
+ //FIXME:KG: This is just an educated guess.
+ // The LAPC-I documentation claims a range of 427.5Hz-452.6Hz (similar to what we have here)
+ // The MT-32 documentation claims a range of 432.1Hz-457.6Hz
+ masterTune = 440.0f * powf(2.0f, (mt32ram.system.masterTune - 64.0f) / (128.0f * 12.0f));
+ printDebug(" Master Tune: %f", masterTune);
+ printDebug(" Reverb: mode=%d, time=%d, level=%d", mt32ram.system.reverbMode, mt32ram.system.reverbTime, mt32ram.system.reverbLevel);
+ report(ReportType_newReverbMode, &mt32ram.system.reverbMode);
+ report(ReportType_newReverbTime, &mt32ram.system.reverbTime);
+ report(ReportType_newReverbLevel, &mt32ram.system.reverbLevel);
+
+ if (myProp.useDefaultReverb) {
+ initReverb(mt32ram.system.reverbMode, mt32ram.system.reverbTime, mt32ram.system.reverbLevel);
+ } else {
+ initReverb(myProp.reverbType, myProp.reverbTime, mt32ram.system.reverbLevel);
+ }
+
+ Bit8u *rset = mt32ram.system.reserveSettings;
+ printDebug(" Partial reserve: 1=%02d 2=%02d 3=%02d 4=%02d 5=%02d 6=%02d 7=%02d 8=%02d Rhythm=%02d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]);
+ int pr = partialManager->setReserve(rset);
+ if (pr != 32)
+ printDebug(" (Partial Reserve Table with less than 32 partials reserved!)");
+ rset = mt32ram.system.chanAssign;
+ printDebug(" Part assign: 1=%02d 2=%02d 3=%02d 4=%02d 5=%02d 6=%02d 7=%02d 8=%02d Rhythm=%02d", rset[0], rset[1], rset[2], rset[3], rset[4], rset[5], rset[6], rset[7], rset[8]);
+ printDebug(" Master volume: %d", mt32ram.system.masterVol);
+ masterVolume = (Bit16u)(mt32ram.system.masterVol * 32767 / 100);
+ if (!tables.init(this, pcmWaves, (float)myProp.sampleRate, masterTune)) {
+ report(ReportType_errorSampleRate, NULL);
+ return false;
+ }
+ return true;
+}
+
+bool Synth::dumpTimbre(File *file, const TimbreParam *timbre, Bit32u address) {
+ // Sysex header
+ if (!file->writeBit8u(0xF0))
+ return false;
+ if (!file->writeBit8u(0x41))
+ return false;
+ if (!file->writeBit8u(0x10))
+ return false;
+ if (!file->writeBit8u(0x16))
+ return false;
+ if (!file->writeBit8u(0x12))
+ return false;
+
+ char lsb = (char)(address & 0x7f);
+ char isb = (char)((address >> 7) & 0x7f);
+ char msb = (char)(((address >> 14) & 0x7f) | 0x08);
+
+ //Address
+ if (!file->writeBit8u(msb))
+ return false;
+ if (!file->writeBit8u(isb))
+ return false;
+ if (!file->writeBit8u(lsb))
+ return false;
+
+ //Data
+ if (file->write(timbre, 246) != 246)
+ return false;
+
+ //Checksum
+ unsigned char checksum = calcSysexChecksum((const Bit8u *)timbre, 246, msb + isb + lsb);
+ if (!file->writeBit8u(checksum))
+ return false;
+
+ //End of sysex
+ if (!file->writeBit8u(0xF7))
+ return false;
+ return true;
+}
+
+int Synth::dumpTimbres(const char *filename, int start, int len) {
+ File *file = openFile(filename, File::OpenMode_write);
+ if (file == NULL)
+ return -1;
+
+ for (int timbreNum = start; timbreNum < start + len; timbreNum++) {
+ int useaddr = (timbreNum - start) * 256;
+ TimbreParam *timbre = &mt32ram.timbres[timbreNum].timbre;
+ if (!dumpTimbre(file, timbre, useaddr))
+ break;
+ }
+ closeFile(file);
+ return 0;
+}
+
+void ProduceOutput1(Bit16s *useBuf, Bit16s *stream, Bit32u len, Bit16s volume) {
+#if MT32EMU_USE_MMX > 2
+ //FIXME:KG: This appears to introduce crackle
+ int donelen = i386_produceOutput1(useBuf, stream, len, volume);
+ len -= donelen;
+ stream += donelen * 2;
+ useBuf += donelen * 2;
+#endif
+ int end = len * 2;
+ while (end--) {
+ *stream = *stream + (Bit16s)(((Bit32s)*useBuf++ * (Bit32s)volume)>>15);
+ stream++;
+ }
+}
+
+void Synth::render(Bit16s *stream, Bit32u len) {
+ memset(stream, 0, len * sizeof (Bit16s) * 2);
+ if (!isEnabled)
+ return;
+ while (len > 0) {
+ Bit32u thisLen = len > MAX_SAMPLE_OUTPUT ? MAX_SAMPLE_OUTPUT : len;
+ doRender(stream, thisLen);
+ len -= thisLen;
+ stream += 2 * thisLen;
+ }
+}
+
+void Synth::doRender(Bit16s *stream, Bit32u len) {
+ partialManager->ageAll();
+
+ if (myProp.useReverb) {
+ for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (partialManager->shouldReverb(i)) {
+ if (partialManager->produceOutput(i, &tmpBuffer[0], len)) {
+ ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume);
+ }
+ }
+ }
+ Bit32u m = 0;
+ for (unsigned int i = 0; i < len; i++) {
+ sndbufl[i] = (float)stream[m] / 32767.0f;
+ m++;
+ sndbufr[i] = (float)stream[m] / 32767.0f;
+ m++;
+ }
+ reverbModel->processreplace(sndbufl, sndbufr, outbufl, outbufr, len, 1);
+ m=0;
+ for (unsigned int i = 0; i < len; i++) {
+ stream[m] = (Bit16s)(outbufl[i] * 32767.0f);
+ m++;
+ stream[m] = (Bit16s)(outbufr[i] * 32767.0f);
+ m++;
+ }
+ for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (!partialManager->shouldReverb(i)) {
+ if (partialManager->produceOutput(i, &tmpBuffer[0], len)) {
+ ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume);
+ }
+ }
+ }
+ } else {
+ for (unsigned int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
+ if (partialManager->produceOutput(i, &tmpBuffer[0], len))
+ ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume);
+ }
+ }
+
+ partialManager->clearAlreadyOutputed();
+
+#if MT32EMU_MONITOR_PARTIALS == 1
+ samplepos += len;
+ if (samplepos > myProp.SampleRate * 5) {
+ samplepos = 0;
+ int partialUsage[9];
+ partialManager->GetPerPartPartialUsage(partialUsage);
+ printDebug("1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d", partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7]);
+ printDebug("Rhythm: %02d TOTAL: %02d", partialUsage[8], MT32EMU_MAX_PARTIALS - partialManager->GetFreePartialCount());
+ }
+#endif
+}
+
+const Partial *Synth::getPartial(unsigned int partialNum) const {
+ return partialManager->getPartial(partialNum);
+}
+
+const Part *Synth::getPart(unsigned int partNum) const {
+ if (partNum > 8)
+ return NULL;
+ return parts[partNum];
+}
+
+}
diff --git a/engines/sci/sfx/softseq/mt32/synth.h b/engines/sci/sfx/softseq/mt32/synth.h
new file mode 100644
index 0000000000..9d57c8d3cd
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/synth.h
@@ -0,0 +1,300 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef MT32EMU_SYNTH_H
+#define MT32EMU_SYNTH_H
+
+#include <stdarg.h>
+
+class revmodel;
+
+namespace MT32Emu {
+
+class File;
+class TableInitialiser;
+class Partial;
+class PartialManager;
+class Part;
+
+enum ReportType {
+ // Errors
+ ReportType_errorControlROM = 1,
+ ReportType_errorPCMROM,
+ ReportType_errorSampleRate,
+
+ // Progress
+ ReportType_progressInit,
+
+ // HW spec
+ ReportType_availableSSE,
+ ReportType_available3DNow,
+ ReportType_usingSSE,
+ ReportType_using3DNow,
+
+ // General info
+ ReportType_lcdMessage,
+ ReportType_devReset,
+ ReportType_devReconfig,
+ ReportType_newReverbMode,
+ ReportType_newReverbTime,
+ ReportType_newReverbLevel
+};
+
+struct SynthProperties {
+ // Sample rate to use in mixing
+ int sampleRate;
+
+ // Flag to activate reverb. True = use reverb, False = no reverb
+ bool useReverb;
+ // True to use software set reverb settings, False to set reverb settings in
+ // following parameters
+ bool useDefaultReverb;
+ // When not using the default settings, this specifies one of the 4 reverb types
+ // 1 = Room 2 = Hall 3 = Plate 4 = Tap
+ unsigned char reverbType;
+ // This specifies the delay time, from 0-7 (not sure of the actual MT-32's measurement)
+ unsigned char reverbTime;
+ // This specifies the reverb level, from 0-7 (not sure of the actual MT-32's measurement)
+ unsigned char reverbLevel;
+ // The name of the directory in which the ROM and data files are stored (with trailing slash/backslash)
+ // Not used if "openFile" is set. May be NULL in any case.
+ char *baseDir;
+ // This is used as the first argument to all callbacks
+ void *userData;
+ // Callback for reporting various errors and information. May be NULL
+ int (*report)(void *userData, ReportType type, const void *reportData);
+ // Callback for debug messages, in vprintf() format
+ void (*printDebug)(void *userData, const char *fmt, va_list list);
+ // Callback for providing an implementation of File, opened and ready for use
+ // May be NULL, in which case a default implementation will be used.
+ File *(*openFile)(void *userData, const char *filename, File::OpenMode mode);
+ // Callback for closing a File. May be NULL, in which case the File will automatically be close()d/deleted.
+ void (*closeFile)(void *userData, File *file);
+};
+
+// This is the specification of the Callback routine used when calling the RecalcWaveforms
+// function
+typedef void (*recalcStatusCallback)(int percDone);
+
+// This external function recreates the base waveform file (waveforms.raw) using a specifed
+// sampling rate. The callback routine provides interactivity to let the user know what
+// percentage is complete in regenerating the waveforms. When a NULL pointer is used as the
+// callback routine, no status is reported.
+bool RecalcWaveforms(char * baseDir, int sampRate, recalcStatusCallback callBack);
+
+typedef float (*iir_filter_type)(float input,float *hist1_ptr, float *coef_ptr);
+
+const Bit8u SYSEX_MANUFACTURER_ROLAND = 0x41;
+
+const Bit8u SYSEX_MDL_MT32 = 0x16;
+const Bit8u SYSEX_MDL_D50 = 0x14;
+
+const Bit8u SYSEX_CMD_RQ1 = 0x11; // Request data #1
+const Bit8u SYSEX_CMD_DT1 = 0x12; // Data set 1
+const Bit8u SYSEX_CMD_WSD = 0x40; // Want to send data
+const Bit8u SYSEX_CMD_RQD = 0x41; // Request data
+const Bit8u SYSEX_CMD_DAT = 0x42; // Data set
+const Bit8u SYSEX_CMD_ACK = 0x43; // Acknowledge
+const Bit8u SYSEX_CMD_EOD = 0x45; // End of data
+const Bit8u SYSEX_CMD_ERR = 0x4E; // Communications error
+const Bit8u SYSEX_CMD_RJC = 0x4F; // Rejection
+
+const unsigned int CONTROL_ROM_SIZE = 64 * 1024;
+
+struct ControlROMPCMStruct
+{
+ Bit8u pos;
+ Bit8u len;
+ Bit8u pitchLSB;
+ Bit8u pitchMSB;
+};
+
+struct ControlROMMap {
+ Bit16u idPos;
+ Bit16u idLen;
+ const char *idBytes;
+ Bit16u pcmTable;
+ Bit16u pcmCount;
+ Bit16u timbreAMap;
+ Bit16u timbreAOffset;
+ Bit16u timbreBMap;
+ Bit16u timbreBOffset;
+ Bit16u timbreRMap;
+ Bit16u timbreRCount;
+ Bit16u rhythmSettings;
+ Bit16u rhythmSettingsCount;
+ Bit16u reserveSettings;
+ Bit16u panSettings;
+ Bit16u programSettings;
+};
+
+enum MemoryRegionType {
+ MR_PatchTemp, MR_RhythmTemp, MR_TimbreTemp, MR_Patches, MR_Timbres, MR_System, MR_Display, MR_Reset
+};
+
+class MemoryRegion {
+public:
+ MemoryRegionType type;
+ Bit32u startAddr, entrySize, entries;
+
+ int lastTouched(Bit32u addr, Bit32u len) const {
+ return (offset(addr) + len - 1) / entrySize;
+ }
+ int firstTouchedOffset(Bit32u addr) const {
+ return offset(addr) % entrySize;
+ }
+ int firstTouched(Bit32u addr) const {
+ return offset(addr) / entrySize;
+ }
+ Bit32u regionEnd() const {
+ return startAddr + entrySize * entries;
+ }
+ bool contains(Bit32u addr) const {
+ return addr >= startAddr && addr < regionEnd();
+ }
+ int offset(Bit32u addr) const {
+ return addr - startAddr;
+ }
+ Bit32u getClampedLen(Bit32u addr, Bit32u len) const {
+ if (addr + len > regionEnd())
+ return regionEnd() - addr;
+ return len;
+ }
+ Bit32u next(Bit32u addr, Bit32u len) const {
+ if (addr + len > regionEnd()) {
+ return regionEnd() - addr;
+ }
+ return 0;
+ }
+};
+
+
+class Synth {
+friend class Part;
+friend class RhythmPart;
+friend class Partial;
+friend class Tables;
+private:
+ bool isEnabled;
+
+ iir_filter_type iirFilter;
+
+ PCMWaveEntry *pcmWaves; // Array
+
+ const ControlROMMap *controlROMMap;
+ Bit8u controlROMData[CONTROL_ROM_SIZE];
+ Bit16s *pcmROMData;
+ int pcmROMSize; // This is in 16-bit samples, therefore half the number of bytes in the ROM
+
+ Bit8s chantable[32];
+
+ #if MT32EMU_MONITOR_PARTIALS == 1
+ static Bit32s samplepos = 0;
+ #endif
+
+ Tables tables;
+
+ MemParams mt32ram, mt32default;
+
+ revmodel *reverbModel;
+
+ float masterTune;
+ Bit16u masterVolume;
+
+ bool isOpen;
+
+ PartialManager *partialManager;
+ Part *parts[9];
+
+ Bit16s tmpBuffer[MAX_SAMPLE_OUTPUT * 2];
+ float sndbufl[MAX_SAMPLE_OUTPUT];
+ float sndbufr[MAX_SAMPLE_OUTPUT];
+ float outbufl[MAX_SAMPLE_OUTPUT];
+ float outbufr[MAX_SAMPLE_OUTPUT];
+
+ SynthProperties myProp;
+
+ bool loadPreset(File *file);
+ void initReverb(Bit8u newRevMode, Bit8u newRevTime, Bit8u newRevLevel);
+ void doRender(Bit16s * stream, Bit32u len);
+
+ void playAddressedSysex(unsigned char channel, const Bit8u *sysex, Bit32u len);
+ void readSysex(unsigned char channel, const Bit8u *sysex, Bit32u len);
+ void writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, const Bit8u *data);
+ void readMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u len, Bit8u *data);
+
+ bool loadControlROM(const char *filename);
+ bool loadPCMROM(const char *filename);
+ bool dumpTimbre(File *file, const TimbreParam *timbre, Bit32u addr);
+ int dumpTimbres(const char *filename, int start, int len);
+
+ bool initPCMList(Bit16u mapAddress, Bit16u count);
+ bool initRhythmTimbres(Bit16u mapAddress, Bit16u count);
+ bool initTimbres(Bit16u mapAddress, Bit16u offset, int startTimbre);
+ bool initRhythmTimbre(int drumNum, const Bit8u *mem, unsigned int memLen);
+ bool refreshSystem();
+
+protected:
+ int report(ReportType type, const void *reportData);
+ File *openFile(const char *filename, File::OpenMode mode);
+ void closeFile(File *file);
+ void printDebug(const char *fmt, ...);
+
+public:
+ static Bit8u calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum);
+
+ Synth();
+ ~Synth();
+
+ // Used to initialise the MT-32. Must be called before any other function.
+ // Returns true if initialization was sucessful, otherwise returns false.
+ bool open(SynthProperties &useProp);
+
+ // Closes the MT-32 and deallocates any memory used by the synthesizer
+ void close(void);
+
+ // Sends a 4-byte MIDI message to the MT-32 for immediate playback
+ void playMsg(Bit32u msg);
+ void playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity);
+
+ // Sends a string of Sysex commands to the MT-32 for immediate interpretation
+ // The length is in bytes
+ void playSysex(const Bit8u *sysex, Bit32u len);
+ void playSysexWithoutFraming(const Bit8u *sysex, Bit32u len);
+ void playSysexWithoutHeader(unsigned char device, unsigned char command, const Bit8u *sysex, Bit32u len);
+ void writeSysex(unsigned char channel, const Bit8u *sysex, Bit32u len);
+
+ // This callback routine is used to have the MT-32 generate samples to the specified
+ // output stream. The length is in whole samples, not bytes. (I.E. in 16-bit stereo,
+ // one sample is 4 bytes)
+ void render(Bit16s * stream, Bit32u len);
+
+ const Partial *getPartial(unsigned int partialNum) const;
+
+ void readMemory(Bit32u addr, Bit32u len, Bit8u *data);
+
+ // partNum should be 0..7 for Part 1..8, or 8 for Rhythm
+ const Part *getPart(unsigned int partNum) const;
+};
+
+}
+
+#endif
diff --git a/engines/sci/sfx/softseq/mt32/tables.cpp b/engines/sci/sfx/softseq/mt32/tables.cpp
new file mode 100644
index 0000000000..4591ea22e3
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/tables.cpp
@@ -0,0 +1,749 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "mt32emu.h"
+
+#ifdef MACOSX
+// Older versions of Mac OS X didn't supply a powf function. To ensure
+// binary compatibility, we force using pow instead of powf (the only
+// potential drawback is that it might be a little bit slower).
+#define powf pow
+#endif
+
+#define FIXEDPOINT_MAKE(x, point) ((Bit32u)((1 << point) * x))
+
+namespace MT32Emu {
+
+//Amplitude time velocity follow exponential coefficients
+static const double tvcatconst[5] = {0.0, 0.002791309, 0.005942882, 0.012652792, 0.026938637};
+static const double tvcatmult[5] = {1.0, 1.072662811, 1.169129367, 1.288579123, 1.229630539};
+
+// These are division constants for the TVF depth key follow
+static const Bit32u depexp[5] = {3000, 950, 485, 255, 138};
+
+//Envelope time keyfollow exponential coefficients
+static const double tkcatconst[5] = {0.0, 0.005853144, 0.011148054, 0.019086143, 0.043333215};
+static const double tkcatmult[5] = {1.0, 1.058245688, 1.048488989, 1.016049301, 1.097538067};
+
+// Begin filter stuff
+
+// Pre-warp the coefficients of a numerator or denominator.
+// Note that a0 is assumed to be 1, so there is no wrapping
+// of it.
+static void prewarp(double *a1, double *a2, double fc, double fs) {
+ double wp;
+
+ wp = 2.0 * fs * tan(DOUBLE_PI * fc / fs);
+
+ *a2 = *a2 / (wp * wp);
+ *a1 = *a1 / wp;
+}
+
+// Transform the numerator and denominator coefficients
+// of s-domain biquad section into corresponding
+// z-domain coefficients.
+//
+// Store the 4 IIR coefficients in array pointed by coef
+// in following order:
+// beta1, beta2 (denominator)
+// alpha1, alpha2 (numerator)
+//
+// Arguments:
+// a0-a2 - s-domain numerator coefficients
+// b0-b2 - s-domain denominator coefficients
+// k - filter gain factor. initially set to 1
+// and modified by each biquad section in such
+// a way, as to make it the coefficient by
+// which to multiply the overall filter gain
+// in order to achieve a desired overall filter gain,
+// specified in initial value of k.
+// fs - sampling rate (Hz)
+// coef - array of z-domain coefficients to be filled in.
+//
+// Return:
+// On return, set coef z-domain coefficients
+static void bilinear(double a0, double a1, double a2, double b0, double b1, double b2, double *k, double fs, float *coef) {
+ double ad, bd;
+
+ // alpha (Numerator in s-domain)
+ ad = 4. * a2 * fs * fs + 2. * a1 * fs + a0;
+ // beta (Denominator in s-domain)
+ bd = 4. * b2 * fs * fs + 2. * b1* fs + b0;
+
+ // update gain constant for this section
+ *k *= ad/bd;
+
+ // Denominator
+ *coef++ = (float)((2. * b0 - 8. * b2 * fs * fs) / bd); // beta1
+ *coef++ = (float)((4. * b2 * fs * fs - 2. * b1 * fs + b0) / bd); // beta2
+
+ // Nominator
+ *coef++ = (float)((2. * a0 - 8. * a2 * fs * fs) / ad); // alpha1
+ *coef = (float)((4. * a2 * fs * fs - 2. * a1 * fs + a0) / ad); // alpha2
+}
+
+// a0-a2: numerator coefficients
+// b0-b2: denominator coefficients
+// fc: Filter cutoff frequency
+// fs: sampling rate
+// k: overall gain factor
+// coef: pointer to 4 iir coefficients
+static void szxform(double *a0, double *a1, double *a2, double *b0, double *b1, double *b2, double fc, double fs, double *k, float *coef) {
+ // Calculate a1 and a2 and overwrite the original values
+ prewarp(a1, a2, fc, fs);
+ prewarp(b1, b2, fc, fs);
+ bilinear(*a0, *a1, *a2, *b0, *b1, *b2, k, fs, coef);
+}
+
+static void initFilter(float fs, float fc, float *icoeff, float Q) {
+ float *coef;
+ double a0, a1, a2, b0, b1, b2;
+
+ double k = 1.5; // Set overall filter gain factor
+ coef = icoeff + 1; // Skip k, or gain
+
+ // Section 1
+ a0 = 1.0;
+ a1 = 0;
+ a2 = 0;
+ b0 = 1.0;
+ b1 = 0.765367 / Q; // Divide by resonance or Q
+ b2 = 1.0;
+ szxform(&a0, &a1, &a2, &b0, &b1, &b2, fc, fs, &k, coef);
+ coef += 4; // Point to next filter section
+
+ // Section 2
+ a0 = 1.0;
+ a1 = 0;
+ a2 = 0;
+ b0 = 1.0;
+ b1 = 1.847759 / Q;
+ b2 = 1.0;
+ szxform(&a0, &a1, &a2, &b0, &b1, &b2, fc, fs, &k, coef);
+
+ icoeff[0] = (float)k;
+}
+
+void Tables::initFiltCoeff(float samplerate) {
+ for (int j = 0; j < FILTERGRAN; j++) {
+ for (int res = 0; res < 31; res++) {
+ float tres = resonanceFactor[res];
+ initFilter((float)samplerate, (((float)(j+1.0)/FILTERGRAN)) * ((float)samplerate/2), filtCoeff[j][res], tres);
+ }
+ }
+}
+
+void Tables::initEnvelopes(float samplerate) {
+ for (int lf = 0; lf <= 100; lf++) {
+ float elf = (float)lf;
+
+ // General envelope
+ // This formula fits observation of the CM-32L by +/- 0.03s or so for the second time value in the filter,
+ // when all other times were 0 and all levels were 100. Note that variations occur depending on the level
+ // delta of the section, which we're not fully emulating.
+ float seconds = powf(2.0f, (elf / 8.0f) + 7.0f) / 32768.0f;
+ int samples = (int)(seconds * samplerate);
+ envTime[lf] = samples;
+
+ // Cap on envelope times depending on the level delta
+ if (elf == 0) {
+ envDeltaMaxTime[lf] = 63;
+ } else {
+ float cap = 11.0f * (float)log(elf) + 64;
+ if (cap > 100.0f) {
+ cap = 100.0f;
+ }
+ envDeltaMaxTime[lf] = (int)cap;
+ }
+
+
+ // This (approximately) represents the time durations when the target level is 0.
+ // Not sure why this is a special case, but it's seen to be from the real thing.
+ seconds = powf(2, (elf / 8.0f) + 6) / 32768.0f;
+ envDecayTime[lf] = (int)(seconds * samplerate);
+
+ // I am certain of this: Verified by hand LFO log
+ lfoPeriod[lf] = (Bit32u)(((float)samplerate) / (powf(1.088883372f, (float)lf) * 0.021236044f));
+ }
+}
+
+void Tables::initMT32ConstantTables(Synth *synth) {
+ int lf;
+ synth->printDebug("Initialising Pitch Tables");
+ for (lf = -108; lf <= 108; lf++) {
+ tvfKeyfollowMult[lf + 108] = (int)(256 * powf(2.0f, (float)(lf / 24.0f)));
+ //synth->printDebug("KT %d = %d", f, keytable[f+108]);
+ }
+
+ for (int res = 0; res < 31; res++) {
+ resonanceFactor[res] = powf((float)res / 30.0f, 5.0f) + 1.0f;
+ }
+
+ int period = 65536;
+
+ for (int ang = 0; ang < period; ang++) {
+ int halfang = (period / 2);
+ int angval = ang % halfang;
+ float tval = (((float)angval / (float)halfang) - 0.5f) * 2;
+ if (ang >= halfang)
+ tval = -tval;
+ sintable[ang] = (Bit16s)(tval * 50.0f) + 50;
+ }
+
+ int velt, dep;
+ float tempdep;
+ for (velt = 0; velt < 128; velt++) {
+ for (dep = 0; dep < 5; dep++) {
+ if (dep > 0) {
+ float ff = (float)(exp(3.5f * tvcatconst[dep] * (59.0f - (float)velt)) * tvcatmult[dep]);
+ tempdep = 256.0f * ff;
+ envTimeVelfollowMult[dep][velt] = (int)tempdep;
+ //if ((velt % 16) == 0) {
+ // synth->printDebug("Key %d, depth %d, factor %d", velt, dep, (int)tempdep);
+ //}
+ } else
+ envTimeVelfollowMult[dep][velt] = 256;
+ }
+
+ for (dep = -7; dep < 8; dep++) {
+ float fldep = (float)abs(dep) / 7.0f;
+ fldep = powf(fldep,2.5f);
+ if (dep < 0)
+ fldep = fldep * -1.0f;
+ pwVelfollowAdd[dep+7][velt] = Bit32s((fldep * (float)velt * 100) / 128.0);
+ }
+ }
+
+ for (dep = 0; dep <= 100; dep++) {
+ for (velt = 0; velt < 128; velt++) {
+ float fdep = (float)dep * 0.000347013f; // Another MT-32 constant
+ float fv = ((float)velt - 64.0f)/7.26f;
+ float flogdep = powf(10, fdep * fv);
+ float fbase;
+
+ if (velt > 64)
+ synth->tables.tvfVelfollowMult[velt][dep] = (int)(flogdep * 256.0);
+ else {
+ //lff = 1 - (pow(((128.0 - (float)lf) / 64.0),.25) * ((float)velt / 96));
+ fbase = 1 - (powf(((float)dep / 100.0f),.25f) * ((float)(64-velt) / 96.0f));
+ synth->tables.tvfVelfollowMult[velt][dep] = (int)(fbase * 256.0);
+ }
+ //synth->printDebug("Filvel dep %d velt %d = %x", dep, velt, filveltable[velt][dep]);
+ }
+ }
+
+ for (lf = 0; lf < 128; lf++) {
+ float veloFract = lf / 127.0f;
+ for (int velsens = 0; velsens <= 100; velsens++) {
+ float sensFract = (velsens - 50) / 50.0f;
+ if (velsens < 50) {
+ tvaVelfollowMult[lf][velsens] = FIXEDPOINT_MAKE(1.0f / powf(2.0f, veloFract * -sensFract * 127.0f / 20.0f), 8);
+ } else {
+ tvaVelfollowMult[lf][velsens] = FIXEDPOINT_MAKE(1.0f / powf(2.0f, (1.0f - veloFract) * sensFract * 127.0f / 20.0f), 8);
+ }
+ }
+ }
+
+ for (lf = 0; lf <= 100; lf++) {
+ // Converts the 0-100 range used by the MT-32 to volume multiplier
+ volumeMult[lf] = FIXEDPOINT_MAKE(powf((float)lf / 100.0f, FLOAT_LN), 7);
+ }
+
+ for (lf = 0; lf <= 100; lf++) {
+ float mv = lf / 100.0f;
+ float pt = mv - 0.5f;
+ if (pt < 0)
+ pt = 0;
+
+ // Original (CC version)
+ //pwFactor[lf] = (int)(pt * 210.04f) + 128;
+
+ // Approximation from sample comparison
+ pwFactor[lf] = (int)(pt * 179.0f) + 128;
+ }
+
+ for (unsigned int i = 0; i < MAX_SAMPLE_OUTPUT; i++) {
+ int myRand;
+ myRand = rand();
+ //myRand = ((myRand - 16383) * 7168) >> 16;
+ // This one is slower but works with all values of RAND_MAX
+ myRand = (int)((myRand - RAND_MAX / 2) / (float)RAND_MAX * (7168 / 2));
+ //FIXME:KG: Original ultimately set the lowest two bits to 0, for no obvious reason
+ noiseBuf[i] = (Bit16s)myRand;
+ }
+
+ float tdist;
+ float padjtable[51];
+ for (lf = 0; lf <= 50; lf++) {
+ if (lf == 0)
+ padjtable[lf] = 7;
+ else if (lf == 1)
+ padjtable[lf] = 6;
+ else if (lf == 2)
+ padjtable[lf] = 5;
+ else if (lf == 3)
+ padjtable[lf] = 4;
+ else if (lf == 4)
+ padjtable[lf] = 4 - (0.333333f);
+ else if (lf == 5)
+ padjtable[lf] = 4 - (0.333333f * 2);
+ else if (lf == 6)
+ padjtable[lf] = 3;
+ else if ((lf > 6) && (lf <= 12)) {
+ tdist = (lf-6.0f) / 6.0f;
+ padjtable[lf] = 3.0f - tdist;
+ } else if ((lf > 12) && (lf <= 25)) {
+ tdist = (lf - 12.0f) / 13.0f;
+ padjtable[lf] = 2.0f - tdist;
+ } else {
+ tdist = (lf - 25.0f) / 25.0f;
+ padjtable[lf] = 1.0f - tdist;
+ }
+ //synth->printDebug("lf %d = padj %f", lf, padjtable[lf]);
+ }
+
+ float lfp, depf, finalval, tlf;
+ int depat, pval, depti;
+ for (lf = 0; lf <= 10; lf++) {
+ // I believe the depth is cubed or something
+
+ for (depat = 0; depat <= 100; depat++) {
+ if (lf > 0) {
+ depti = abs(depat - 50);
+ tlf = (float)lf - padjtable[depti];
+ if (tlf < 0)
+ tlf = 0;
+ lfp = (float)exp(0.713619942f * tlf) / 407.4945111f;
+
+ if (depat < 50)
+ finalval = 4096.0f * powf(2, -lfp);
+ else
+ finalval = 4096.0f * powf(2, lfp);
+ pval = (int)finalval;
+
+ pitchEnvVal[lf][depat] = pval;
+ //synth->printDebug("lf %d depat %d pval %d tlf %f lfp %f", lf,depat,pval, tlf, lfp);
+ } else {
+ pitchEnvVal[lf][depat] = 4096;
+ //synth->printDebug("lf %d depat %d pval 4096", lf, depat);
+ }
+ }
+ }
+ for (lf = 0; lf <= 100; lf++) {
+ // It's linear - verified on MT-32 - one of the few things linear
+ lfp = ((float)lf * 0.1904f) / 310.55f;
+
+ for (depat = 0; depat <= 100; depat++) {
+ depf = ((float)depat - 50.0f) / 50.0f;
+ //finalval = pow(2, lfp * depf * .5);
+ finalval = 4096.0f + (4096.0f * lfp * depf);
+
+ pval = (int)finalval;
+
+ lfoShift[lf][depat] = pval;
+
+ //synth->printDebug("lf %d depat %d pval %x", lf,depat,pval);
+ }
+ }
+
+ for (lf = 0; lf <= 12; lf++) {
+ for (int distval = 0; distval < 128; distval++) {
+ float amplog, dval;
+ if (lf == 0) {
+ amplog = 0;
+ dval = 1;
+ tvaBiasMult[lf][distval] = 256;
+ } else {
+ /*
+ amplog = powf(1.431817011f, (float)lf) / FLOAT_PI;
+ dval = ((128.0f - (float)distval) / 128.0f);
+ amplog = exp(amplog);
+ dval = powf(amplog, dval) / amplog;
+ tvaBiasMult[lf][distval] = (int)(dval * 256.0);
+ */
+ // Lets assume for a second it's linear
+
+ // Distance of full volume reduction
+ amplog = (float)(12.0f / (float)lf) * 24.0f;
+ if (distval > amplog) {
+ tvaBiasMult[lf][distval] = 0;
+ } else {
+ dval = (amplog - (float)distval) / amplog;
+ tvaBiasMult[lf][distval] = (int)(dval * 256.0f);
+ }
+ }
+ //synth->printDebug("Ampbias lf %d distval %d = %f (%x) %f", lf, distval, dval, tvaBiasMult[lf][distval],amplog);
+ }
+ }
+
+ for (lf = 0; lf <= 14; lf++) {
+ for (int distval = 0; distval < 128; distval++) {
+ float filval = fabsf((float)((lf - 7) * 12) / 7.0f);
+ float amplog, dval;
+ if (lf == 7) {
+ amplog = 0;
+ dval = 1;
+ tvfBiasMult[lf][distval] = 256;
+ } else {
+ //amplog = pow(1.431817011, filval) / FLOAT_PI;
+ amplog = powf(1.531817011f, filval) / FLOAT_PI;
+ dval = (128.0f - (float)distval) / 128.0f;
+ amplog = (float)exp(amplog);
+ dval = powf(amplog,dval)/amplog;
+ if (lf < 8) {
+ tvfBiasMult[lf][distval] = (int)(dval * 256.0f);
+ } else {
+ dval = powf(dval, 0.3333333f);
+ if (dval < 0.01f)
+ dval = 0.01f;
+ dval = 1 / dval;
+ tvfBiasMult[lf][distval] = (int)(dval * 256.0f);
+ }
+ }
+ //synth->printDebug("Fbias lf %d distval %d = %f (%x) %f", lf, distval, dval, tvfBiasMult[lf][distval],amplog);
+ }
+ }
+}
+
+// Per-note table initialisation follows
+
+static void initSaw(NoteLookup *noteLookup, Bit32s div2) {
+ int tmpdiv = div2 << 16;
+ for (int rsaw = 0; rsaw <= 100; rsaw++) {
+ float fsaw;
+ if (rsaw < 50)
+ fsaw = 50.0f;
+ else
+ fsaw = (float)rsaw;
+
+ //(66 - (((A8 - 50) / 50) ^ 0.63) * 50) / 132
+ float sawfact = (66.0f - (powf((fsaw - 50.0f) / 50.0f, 0.63f) * 50.0f)) / 132.0f;
+ noteLookup->sawTable[rsaw] = (int)(sawfact * (float)tmpdiv) >> 16;
+ //synth->printDebug("F %d divtable %d saw %d sawtable %d", f, div, rsaw, sawtable[f][rsaw]);
+ }
+}
+
+static void initDep(KeyLookup *keyLookup, float f) {
+ for (int dep = 0; dep < 5; dep++) {
+ if (dep == 0) {
+ keyLookup->envDepthMult[dep] = 256;
+ keyLookup->envTimeMult[dep] = 256;
+ } else {
+ float depfac = 3000.0f;
+ float ff, tempdep;
+ depfac = (float)depexp[dep];
+
+ ff = (f - (float)MIDDLEC) / depfac;
+ tempdep = powf(2, ff) * 256.0f;
+ keyLookup->envDepthMult[dep] = (int)tempdep;
+
+ ff = (float)(exp(tkcatconst[dep] * ((float)MIDDLEC - f)) * tkcatmult[dep]);
+ keyLookup->envTimeMult[dep] = (int)(ff * 256.0f);
+ }
+ }
+ //synth->printDebug("F %f d1 %x d2 %x d3 %x d4 %x d5 %x", f, noteLookup->fildepTable[0], noteLookup->fildepTable[1], noteLookup->fildepTable[2], noteLookup->fildepTable[3], noteLookup->fildepTable[4]);
+}
+
+Bit16s Tables::clampWF(Synth *synth, const char *n, float ampVal, double input) {
+ Bit32s x = (Bit32s)(input * ampVal);
+ if (x < -ampVal - 1) {
+ synth->printDebug("%s==%d<-WGAMP-1!", n, x);
+ x = (Bit32s)(-ampVal - 1);
+ } else if (x > ampVal) {
+ synth->printDebug("%s==%d>WGAMP!", n, x);
+ x = (Bit32s)ampVal;
+ }
+ return (Bit16s)x;
+}
+
+File *Tables::initWave(Synth *synth, NoteLookup *noteLookup, float ampVal, float div2, File *file) {
+ int iDiv2 = (int)div2;
+ noteLookup->waveformSize[0] = iDiv2 << 1;
+ noteLookup->waveformSize[1] = iDiv2 << 1;
+ noteLookup->waveformSize[2] = iDiv2 << 2;
+ for (int i = 0; i < 3; i++) {
+ if (noteLookup->waveforms[i] == NULL) {
+ noteLookup->waveforms[i] = new Bit16s[noteLookup->waveformSize[i]];
+ }
+ }
+ if (file != NULL) {
+ for (int i = 0; i < 3 && file != NULL; i++) {
+ size_t len = noteLookup->waveformSize[i];
+ for (unsigned int j = 0; j < len; j++) {
+ if (!file->readBit16u((Bit16u *)&noteLookup->waveforms[i][j])) {
+ synth->printDebug("Error reading wave file cache!");
+ file->close();
+ file = NULL;
+ break;
+ }
+ }
+ }
+ }
+ if (file == NULL) {
+ double sd = DOUBLE_PI / div2;
+
+ for (int fa = 0; fa < (iDiv2 << 1); fa++) {
+ // sa ranges from 0 to 2PI
+ double sa = fa * sd;
+
+ // Calculate a sample for the bandlimited sawtooth wave
+ double saw = 0.0;
+ int sincs = iDiv2 >> 1;
+ double sinus = 1.0;
+ for (int sincNum = 1; sincNum <= sincs; sincNum++) {
+ saw += sin(sinus * sa) / sinus;
+ sinus++;
+ }
+
+ // This works pretty well
+ // Multiplied by 0.84 so that the spikes caused by bandlimiting don't overdrive the amplitude
+ noteLookup->waveforms[0][fa] = clampWF(synth, "saw", ampVal, -saw / (0.5 * DOUBLE_PI) * 0.84);
+ noteLookup->waveforms[1][fa] = clampWF(synth, "cos", ampVal, -cos(sa / 2.0));
+ noteLookup->waveforms[2][fa * 2] = clampWF(synth, "cosoff_0", ampVal, -cos(sa - DOUBLE_PI));
+ noteLookup->waveforms[2][fa * 2 + 1] = clampWF(synth, "cosoff_1", ampVal, -cos((sa + (sd / 2)) - DOUBLE_PI));
+ }
+ }
+ return file;
+}
+
+static void initFiltTable(NoteLookup *noteLookup, float freq, float rate) {
+ for (int tr = 0; tr <= 200; tr++) {
+ float ftr = (float)tr;
+
+ // Verified exact on MT-32
+ if (tr > 100)
+ ftr = 100.0f + (powf((ftr - 100.0f) / 100.0f, 3.0f) * 100.0f);
+
+ // I think this is the one
+ float brsq = powf(10.0f, (tr / 50.0f) - 1.0f);
+ noteLookup->filtTable[0][tr] = (int)((freq * brsq) / (rate / 2) * FILTERGRAN);
+ if (noteLookup->filtTable[0][tr]>=((FILTERGRAN*15)/16))
+ noteLookup->filtTable[0][tr] = ((FILTERGRAN*15)/16);
+
+ float brsa = powf(10.0f, ((tr / 55.0f) - 1.0f)) / 2.0f;
+ noteLookup->filtTable[1][tr] = (int)((freq * brsa) / (rate / 2) * FILTERGRAN);
+ if (noteLookup->filtTable[1][tr]>=((FILTERGRAN*15)/16))
+ noteLookup->filtTable[1][tr] = ((FILTERGRAN*15)/16);
+ }
+}
+
+static void initNFiltTable(NoteLookup *noteLookup, float freq, float rate) {
+ for (int cf = 0; cf <= 100; cf++) {
+ float cfmult = (float)cf;
+
+ for (int tf = 0;tf <= 100; tf++) {
+ float tfadd = (float)tf;
+
+ //float freqsum = exp((cfmult + tfadd) / 30.0f) / 4.0f;
+ //float freqsum = 0.15f * exp(0.45f * ((cfmult + tfadd) / 10.0f));
+
+ float freqsum = powf(2.0f, ((cfmult + tfadd) - 40.0f) / 16.0f);
+
+ noteLookup->nfiltTable[cf][tf] = (int)((freq * freqsum) / (rate / 2) * FILTERGRAN);
+ if (noteLookup->nfiltTable[cf][tf] >= ((FILTERGRAN * 15) / 16))
+ noteLookup->nfiltTable[cf][tf] = ((FILTERGRAN * 15) / 16);
+ }
+ }
+}
+
+File *Tables::initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float masterTune, PCMWaveEntry *pcmWaves, File *file) {
+ float freq = (float)(masterTune * pow(2.0, ((double)note - MIDDLEA) / 12.0));
+ float div2 = rate * 2.0f / freq;
+ noteLookup->div2 = (int)div2;
+
+ if (noteLookup->div2 == 0)
+ noteLookup->div2 = 1;
+
+ initSaw(noteLookup, noteLookup->div2);
+
+ //synth->printDebug("Note %f; freq=%f, div=%f", note, freq, rate / freq);
+ file = initWave(synth, noteLookup, (const float)WGAMP, div2, file);
+
+ // Create the pitch tables
+ if (noteLookup->wavTable == NULL)
+ noteLookup->wavTable = new Bit32u[synth->controlROMMap->pcmCount];
+ double rateMult = 32000.0 / rate;
+ double tuner = freq * 65536.0f;
+ for (int pc = 0; pc < synth->controlROMMap->pcmCount; pc++) {
+ noteLookup->wavTable[pc] = (int)(tuner / pcmWaves[pc].tune * rateMult);
+ }
+
+ initFiltTable(noteLookup, freq, rate);
+ initNFiltTable(noteLookup, freq, rate);
+ return file;
+}
+
+bool Tables::initNotes(Synth *synth, PCMWaveEntry *pcmWaves, float rate, float masterTune) {
+ const char *NoteNames[12] = {
+ "C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B "
+ };
+ char filename[64];
+ int intRate = (int)rate;
+ char version[4] = {0, 0, 0, 5};
+ sprintf(filename, "waveformcache-%d-%.2f.raw", intRate, masterTune);
+
+ File *file = NULL;
+ char header[20];
+ strncpy(header, "MT32WAVE", 8);
+ int pos = 8;
+ // Version...
+ for (int i = 0; i < 4; i++)
+ header[pos++] = version[i];
+ header[pos++] = (char)((intRate >> 24) & 0xFF);
+ header[pos++] = (char)((intRate >> 16) & 0xFF);
+ header[pos++] = (char)((intRate >> 8) & 0xFF);
+ header[pos++] = (char)(intRate & 0xFF);
+ int intTuning = (int)masterTune;
+ header[pos++] = (char)((intTuning >> 8) & 0xFF);
+ header[pos++] = (char)(intTuning & 0xFF);
+ header[pos++] = 0;
+ header[pos] = (char)((masterTune - intTuning) * 10);
+#if MT32EMU_WAVECACHEMODE < 2
+ bool reading = false;
+ file = synth->openFile(filename, File::OpenMode_read);
+ if (file != NULL) {
+ char fileHeader[20];
+ if (file->read(fileHeader, 20) == 20) {
+ if (memcmp(fileHeader, header, 20) == 0) {
+ Bit16u endianCheck;
+ if (file->readBit16u(&endianCheck)) {
+ if (endianCheck == 1) {
+ reading = true;
+ } else {
+ synth->printDebug("Endian check in %s does not match expected", filename);
+ }
+ } else {
+ synth->printDebug("Unable to read endian check in %s", filename);
+ }
+ } else {
+ synth->printDebug("Header of %s does not match expected", filename);
+ }
+ } else {
+ synth->printDebug("Error reading 16 bytes of %s", filename);
+ }
+ if (!reading) {
+ file->close();
+ file = NULL;
+ }
+ } else {
+ synth->printDebug("Unable to open %s for reading", filename);
+ }
+#endif
+
+ float progress = 0.0f;
+ bool abort = false;
+ synth->report(ReportType_progressInit, &progress);
+ for (int f = LOWEST_NOTE; f <= HIGHEST_NOTE; f++) {
+ synth->printDebug("Initialising note %s%d", NoteNames[f % 12], (f / 12) - 2);
+ NoteLookup *noteLookup = &noteLookups[f - LOWEST_NOTE];
+ file = initNote(synth, noteLookup, (float)f, rate, masterTune, pcmWaves, file);
+ progress = (f - LOWEST_NOTE + 1) / (float)NUM_NOTES;
+ abort = synth->report(ReportType_progressInit, &progress) != 0;
+ if (abort)
+ break;
+ }
+
+#if MT32EMU_WAVECACHEMODE == 0 || MT32EMU_WAVECACHEMODE == 2
+ if (file == NULL) {
+ file = synth->openFile(filename, File::OpenMode_write);
+ if (file != NULL) {
+ if (file->write(header, 20) == 20 && file->writeBit16u(1)) {
+ for (int f = 0; f < NUM_NOTES; f++) {
+ for (int i = 0; i < 3 && file != NULL; i++) {
+ int len = noteLookups[f].waveformSize[i];
+ for (int j = 0; j < len; j++) {
+ if (!file->writeBit16u(noteLookups[f].waveforms[i][j])) {
+ synth->printDebug("Error writing waveform cache file");
+ file->close();
+ file = NULL;
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ synth->printDebug("Error writing 16-byte header to %s - won't continue saving", filename);
+ }
+ } else {
+ synth->printDebug("Unable to open %s for writing - won't be created", filename);
+ }
+ }
+#endif
+
+ if (file != NULL)
+ synth->closeFile(file);
+ return !abort;
+}
+
+void Tables::freeNotes() {
+ for (int t = 0; t < 3; t++) {
+ for (int m = 0; m < NUM_NOTES; m++) {
+ if (noteLookups[m].waveforms[t] != NULL) {
+ delete[] noteLookups[m].waveforms[t];
+ noteLookups[m].waveforms[t] = NULL;
+ noteLookups[m].waveformSize[t] = 0;
+ }
+ if (noteLookups[m].wavTable != NULL) {
+ delete[] noteLookups[m].wavTable;
+ noteLookups[m].wavTable = NULL;
+ }
+ }
+ }
+ initialisedMasterTune = 0.0f;
+}
+
+Tables::Tables() {
+ initialisedSampleRate = 0.0f;
+ initialisedMasterTune = 0.0f;
+ memset(&noteLookups, 0, sizeof(noteLookups));
+}
+
+bool Tables::init(Synth *synth, PCMWaveEntry *pcmWaves, float sampleRate, float masterTune) {
+ if (sampleRate <= 0.0f) {
+ synth->printDebug("Bad sampleRate (%d <= 0.0f)", sampleRate);
+ return false;
+ }
+ if (initialisedSampleRate == 0.0f) {
+ initMT32ConstantTables(synth);
+ }
+ if (initialisedSampleRate != sampleRate) {
+ initFiltCoeff(sampleRate);
+ initEnvelopes(sampleRate);
+ for (int key = 12; key <= 108; key++) {
+ initDep(&keyLookups[key - 12], (float)key);
+ }
+ }
+ if (initialisedSampleRate != sampleRate || initialisedMasterTune != masterTune) {
+ freeNotes();
+ if (!initNotes(synth, pcmWaves, sampleRate, masterTune)) {
+ return false;
+ }
+ initialisedSampleRate = sampleRate;
+ initialisedMasterTune = masterTune;
+ }
+ return true;
+}
+
+}
diff --git a/engines/sci/sfx/softseq/mt32/tables.h b/engines/sci/sfx/softseq/mt32/tables.h
new file mode 100644
index 0000000000..d9af5114b2
--- /dev/null
+++ b/engines/sci/sfx/softseq/mt32/tables.h
@@ -0,0 +1,116 @@
+/* Copyright (c) 2003-2005 Various contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef MT32EMU_TABLES_H
+#define MT32EMU_TABLES_H
+
+namespace MT32Emu {
+
+// Mathematical constants
+const double DOUBLE_PI = 3.1415926535897932384626433832795;
+const double DOUBLE_LN = 2.3025850929940456840179914546844;
+const float FLOAT_PI = 3.1415926535897932384626433832795f;
+const float FLOAT_LN = 2.3025850929940456840179914546844f;
+
+// Filter settings
+const int FILTERGRAN = 512;
+
+// Amplitude of waveform generator
+// FIXME: This value is the amplitude possible whilst avoiding
+// overdriven values immediately after filtering when playing
+// back SQ3MT.MID. Needs to be checked.
+const int WGAMP = 12382;
+
+const int MIDDLEC = 60;
+const int MIDDLEA = 69; // By this I mean "A above middle C"
+
+// FIXME:KG: may only need to do 12 to 108
+// 12..108 is the range allowed by note on commands, but the key can be modified by pitch keyfollow
+// and adjustment for timbre pitch, so the results can be outside that range.
+// Should we move it (by octave) into the 12..108 range, or keep it in 0..127 range,
+// or something else altogether?
+const int LOWEST_NOTE = 12;
+const int HIGHEST_NOTE = 127;
+const int NUM_NOTES = HIGHEST_NOTE - LOWEST_NOTE + 1; // Number of slots for note LUT
+
+class Synth;
+
+struct NoteLookup {
+ Bit32u div2;
+ Bit32u *wavTable;
+ Bit32s sawTable[101];
+ int filtTable[2][201];
+ int nfiltTable[101][101];
+ Bit16s *waveforms[3];
+ Bit32u waveformSize[3];
+};
+
+struct KeyLookup {
+ Bit32s envTimeMult[5]; // For envelope time adjustment for key pressed
+ Bit32s envDepthMult[5];
+};
+
+class Tables {
+ float initialisedSampleRate;
+ float initialisedMasterTune;
+ void initMT32ConstantTables(Synth *synth);
+ static Bit16s clampWF(Synth *synth, const char *n, float ampVal, double input);
+ static File *initWave(Synth *synth, NoteLookup *noteLookup, float ampsize, float div2, File *file);
+ bool initNotes(Synth *synth, PCMWaveEntry *pcmWaves, float rate, float tuning);
+ void initEnvelopes(float sampleRate);
+ void initFiltCoeff(float samplerate);
+public:
+ // Constant LUTs
+ Bit32s tvfKeyfollowMult[217];
+ Bit32s tvfVelfollowMult[128][101];
+ Bit32s tvfBiasMult[15][128];
+ Bit32u tvaVelfollowMult[128][101];
+ Bit32s tvaBiasMult[13][128];
+ Bit16s noiseBuf[MAX_SAMPLE_OUTPUT];
+ Bit16s sintable[65536];
+ Bit32s pitchEnvVal[16][101];
+ Bit32s envTimeVelfollowMult[5][128];
+ Bit32s pwVelfollowAdd[15][128];
+ float resonanceFactor[31];
+ Bit32u lfoShift[101][101];
+ Bit32s pwFactor[101];
+ Bit32s volumeMult[101];
+
+ // LUTs varying with sample rate
+ Bit32u envTime[101];
+ Bit32u envDeltaMaxTime[101];
+ Bit32u envDecayTime[101];
+ Bit32u lfoPeriod[101];
+ float filtCoeff[FILTERGRAN][31][8];
+
+ // Various LUTs for each note and key
+ NoteLookup noteLookups[NUM_NOTES];
+ KeyLookup keyLookups[97];
+
+ Tables();
+ bool init(Synth *synth, PCMWaveEntry *pcmWaves, float sampleRate, float masterTune);
+ File *initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float tuning, PCMWaveEntry *pcmWaves, File *file);
+ void freeNotes();
+};
+
+}
+
+#endif
diff --git a/engines/sci/sfx/softseq/opl2.c b/engines/sci/sfx/softseq/opl2.c
new file mode 100644
index 0000000000..ed2f3001c8
--- /dev/null
+++ b/engines/sci/sfx/softseq/opl2.c
@@ -0,0 +1,718 @@
+/***************************************************************************
+ opl2.c Copyright (C) 2002/04 Solomon Peachy, Christoph Reichenbach
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+***************************************************************************/
+
+#include <resource.h>
+#include <sfx_iterator.h>
+#include "../softseq.h"
+#include "../adlib.h"
+#include <math.h>
+
+#include "fmopl.h"
+
+#ifdef _DREAMCAST
+#define SAMPLE_RATE 22050
+#define CHANNELS SFX_PCM_MONO
+#define STEREO 0
+#else
+#define SAMPLE_RATE 44100
+#define CHANNELS SFX_PCM_STEREO_LR
+#define STEREO 1
+#endif
+
+
+/* local function declarations */
+
+static void opl2_allstop(sfx_softseq_t *self);
+
+//#define DEBUG_ADLIB
+
+/* portions shamelessly lifted from claudio's XMP */
+/* other portions lifted from sound/opl3.c in the Linux kernel */
+
+#define ADLIB_LEFT 0
+#define ADLIB_RIGHT 1
+
+/* #define OPL_INTERNAL_FREQ 3600000 */
+#define OPL_INTERNAL_FREQ 3579545
+
+static int ready = 0;
+static int pcmout_stereo = STEREO;
+
+static int register_base[11] = {
+ 0x20, 0x23, 0x40, 0x43,
+ 0x60, 0x63, 0x80, 0x83,
+ 0xe0, 0xe3, 0xc0
+};
+
+static int register_offset[12] = {
+ /* Channel 1 2 3 4 5 6 7 8 9 */
+ /* Operator 1 */ 0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12, 0x18, 0x19, 0x1A
+
+};
+
+static int ym3812_note[13] = {
+ 0x157, 0x16b, 0x181, 0x198, 0x1b0, 0x1ca,
+ 0x1e5, 0x202, 0x220, 0x241, 0x263, 0x287,
+ 0x2ae
+};
+
+static guint8 sci_adlib_vol_base[16] = {
+ 0x00, 0x11, 0x15, 0x19, 0x1D, 0x22, 0x26, 0x2A,
+ 0x2E, 0x23, 0x37, 0x3B, 0x3F, 0x3F, 0x3F, 0x3F
+};
+static guint8 sci_adlib_vol_tables[16][64];
+
+/* ripped out of the linux kernel, of all places. */
+static gint8 fm_volume_table[128] = {
+ -64, -48, -40, -35, -32, -29, -27, -26,
+ -24, -23, -21, -20, -19, -18, -18, -17,
+ -16, -15, -15, -14, -13, -13, -12, -12,
+ -11, -11, -10, -10, -10, -9, -9, -8,
+ -8, -8, -7, -7, -7, -6, -6, -6,
+ -5, -5, -5, -5, -4, -4, -4, -4,
+ -3, -3, -3, -3, -2, -2, -2, -2,
+ -2, -1, -1, -1, -1, 0, 0, 0,
+ 0, 0, 0, 1, 1, 1, 1, 1,
+ 1, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 4,
+ 4, 4, 4, 4, 4, 4, 4, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 8, 8, 8, 8, 8
+};
+
+/* back to your regularly scheduled definitions */
+
+static guint8 instr[MIDI_CHANNELS];
+static guint16 pitch[MIDI_CHANNELS];
+static guint8 vol[MIDI_CHANNELS];
+static guint8 pan[MIDI_CHANNELS];
+static int free_voices = ADLIB_VOICES;
+static guint8 oper_note[ADLIB_VOICES];
+static guint8 oper_chn[ADLIB_VOICES];
+
+static FM_OPL *ym3812_L = NULL;
+static FM_OPL *ym3812_R = NULL;
+
+static guint8 adlib_reg_L[256];
+static guint8 adlib_reg_R[256];
+static guint8 adlib_master;
+
+
+/* initialise note/operator lists, etc. */
+void adlibemu_init_lists(void)
+{
+ int i;
+
+ int j;
+
+ for (i = 0 ; i < 16 ; i++) {
+ for (j = 0; j < 64 ; j++) {
+ sci_adlib_vol_tables[i][j] = ((guint16)sci_adlib_vol_base[i]) * j / 63;
+ }
+ }
+
+ for (i = 0; i < MIDI_CHANNELS ; i++) {
+ pitch[i] = 8192; /* center the pitch wheel */
+ }
+
+ free_voices = ADLIB_VOICES;
+
+ memset(instr, 0, sizeof(instr));
+ memset(vol, 0x7f, sizeof(vol));
+ memset(pan, 0x3f, sizeof(pan));
+ memset(adlib_reg_L, 0, sizeof(adlib_reg_L));
+ memset(adlib_reg_R, 0, sizeof(adlib_reg_R));
+ memset(oper_chn, 0xff, sizeof(oper_chn));
+ memset(oper_note, 0xff, sizeof(oper_note));
+ adlib_master=12;
+}
+
+/* more shamelessly lifted from xmp and adplug. And altered. :) */
+
+static inline int opl_write_L (int a, int v)
+{
+ adlib_reg_L[a] = v;
+ OPLWrite (ym3812_L, 0x388, a);
+ return OPLWrite (ym3812_L, 0x389, v);
+}
+
+static inline int opl_write_R (int a, int v)
+{
+ adlib_reg_R[a] = v;
+ OPLWrite (ym3812_R, 0x388, a);
+ return OPLWrite (ym3812_R, 0x389, v);
+}
+
+static inline int opl_write(int a, int v)
+{
+ opl_write_L(a,v);
+ return opl_write_R(a,v);
+}
+
+/*
+static inline guint8 opl_read (int a)
+{
+ OPLWrite (ym3812_L, 0x388, a);
+ return OPLRead (ym3812_L, 0x389);
+}
+*/
+
+void synth_setpatch (int voice, guint8 *data)
+{
+ int i;
+
+ opl_write(0xBD, 0);
+
+ for (i = 0; i < 10; i++)
+ opl_write(register_base[i] + register_offset[voice], data[i]);
+
+ opl_write(register_base[10] + voice, data[10]);
+
+ /* mute voice after patch change */
+ opl_write_L(0xb0 + voice, adlib_reg_L[0xb0+voice] & 0xdf);
+ opl_write_R(0xb0 + voice, adlib_reg_R[0xb0+voice] & 0xdf);
+
+#ifdef DEBUG_ADLIB
+ for (i = 0; i < 10; i++)
+ printf("%02x ", adlib_reg_L[register_base[i]+register_offset[voice]]);
+ printf("%02x ", adlib_reg_L[register_base[10]+voice]);
+#endif
+
+}
+
+void synth_setvolume_L (int voice, int volume)
+{
+ gint8 level1, level2;
+
+ level1 = ~adlib_reg_L[register_base[2]+register_offset[voice]] & 0x3f;
+ level2 = ~adlib_reg_L[register_base[3]+register_offset[voice]] & 0x3f;
+
+ if (level1) {
+ level1 += sci_adlib_vol_tables[adlib_master][volume>>1];
+ }
+
+ if (level2) {
+ level2 += sci_adlib_vol_tables[adlib_master][volume>>1];
+ }
+
+ if (level1 > 0x3f)
+ level1 = 0x3f;
+ if (level1 < 0)
+ level1 = 0;
+
+ if (level2 > 0x3f)
+ level2 = 0x3f;
+ if (level2 < 0)
+ level2 = 0;
+
+ /* algorithm-dependent; we may need to set both operators. */
+ if (adlib_reg_L[register_base[10]+voice] & 1)
+ opl_write_L(register_base[2]+register_offset[voice],
+ (guint8)((~level1 &0x3f) |
+ (adlib_reg_L[register_base[2]+register_offset[voice]]&0xc0)));
+
+ opl_write_L(register_base[3]+register_offset[voice],
+ (guint8)((~level2 &0x3f) |
+ (adlib_reg_L[register_base[3]+register_offset[voice]]&0xc0)));
+
+}
+
+void synth_setvolume_R (int voice, int volume)
+{
+ gint8 level1, level2;
+
+ level1 = ~adlib_reg_R[register_base[2]+register_offset[voice]] & 0x3f;
+ level2 = ~adlib_reg_R[register_base[3]+register_offset[voice]] & 0x3f;
+
+ if (level1) {
+ level1 += sci_adlib_vol_tables[adlib_master][volume>>1];
+ }
+
+ if (level2) {
+ level2 += sci_adlib_vol_tables[adlib_master][volume>>1];
+ }
+
+ if (level1 > 0x3f)
+ level1 = 0x3f;
+ if (level1 < 0)
+ level1 = 0;
+
+ if (level2 > 0x3f)
+ level2 = 0x3f;
+ if (level2 < 0)
+ level2 = 0;
+
+ /* now for the other side. */
+ if (adlib_reg_R[register_base[10]+voice] & 1)
+ opl_write_R(register_base[2]+register_offset[voice],
+ (guint8)((~level1 &0x3f) |
+ (adlib_reg_R[register_base[2]+register_offset[voice]]&0xc0)));
+
+ opl_write_R(register_base[3]+register_offset[voice],
+ (guint8)((~level2 &0x3f) |
+ (adlib_reg_R[register_base[3]+register_offset[voice]]&0xc0)));
+
+}
+
+void synth_setnote (int voice, int note, int bend)
+{
+ int n, fre, oct;
+ float delta;
+
+ delta = 0;
+
+ n = note % 12;
+
+ if (bend < 8192)
+ bend = 8192-bend;
+ delta = pow(2, (float) (bend%8192)/8192.0);
+
+ if (bend > 8192)
+ fre = ym3812_note[n]*delta; else
+ fre = ym3812_note[n]/delta;
+
+ oct = note / 12 - 1;
+
+ if (oct < 0)
+ oct = 0;
+
+ opl_write(0xa0 + voice, fre & 0xff);
+ opl_write(0xb0 + voice,
+ 0x20 | ((oct << 2) & 0x1c) | ((fre >> 8) & 0x03));
+#ifdef DEBUG_ADLIB
+ printf("-- %02x %02x\n", adlib_reg_L[0xa0+voice], adlib_reg_L[0xb0+voice]);
+#endif
+
+}
+
+
+/* back to your regularly scheduled driver */
+
+int adlibemu_stop_note(int chn, int note, int velocity)
+{
+ int i, op=255;
+
+ // sciprintf("Note off %d %d %d\n", chn, note, velocity);
+
+ for (i=0;i<ADLIB_VOICES && op==255;i++) {
+ if (oper_chn[i] == chn)
+ if (oper_note[i] == note)
+ op=i;
+ }
+
+ if (op==255) {
+#ifdef DEBUG_ADLIB
+ printf ("ADLIB: can't stop note: C%02x N%02x V%02x\n", chn, note, velocity);
+ printf ("C ");
+ for (i = 0; i < ADLIB_VOICES ; i++ ) {
+ printf ("%02x ", oper_chn[i]);
+ }
+ printf ("\n");
+ printf ("N ");
+ for (i = 0; i < ADLIB_VOICES ; i++ ) {
+ printf ("%02x ", oper_note[i]);
+ }
+ printf ("\n");
+#endif
+ return -1; /* that note isn't playing.. */
+ }
+
+ opl_write_L(0xb0+op,(adlib_reg_L[0xb0+op] & 0xdf));
+ opl_write_R(0xb0+op,(adlib_reg_R[0xb0+op] & 0xdf));
+
+ oper_chn[op] = 255;
+ oper_note[op] = 255;
+
+ free_voices++;
+
+#ifdef DEBUG_ADLIB
+ printf("stop voice %d (%d rem): C%02x N%02x V%02x\n", op, free_voices, chn, note, velocity);
+#endif
+
+ return 0;
+}
+
+int adlibemu_start_note(int chn, int note, int velocity)
+{
+ int op, volume_L, volume_R, inst = 0;
+
+ // sciprintf("Note on %d %d %d\n", chn, note, velocity);
+
+ if (velocity == 0) {
+ return adlibemu_stop_note(chn, note, velocity);
+ }
+
+ if (free_voices <= 0) {
+ printf("ADLIB: All voices full\n"); /* XXX implement overflow code */
+ return -1;
+ }
+
+ for (op = 0; op < ADLIB_VOICES ; op++)
+ if (oper_chn[op] == 255)
+ break;
+
+ if (op == ADLIB_VOICES) {
+ printf("ADLIB: WTF? We couldn't find a voice yet it we have %d left.\n", free_voices);
+ return -1;
+ }
+
+ /* Scale channel volume */
+ volume_L = velocity * vol[chn] / 127;
+ volume_R = velocity * vol[chn] / 127;
+
+ /* Apply a pan */
+ if (pcmout_stereo) {
+ if (pan[chn] > 0x3f) /* pan right; so we scale the left down. */
+ volume_L = volume_L / 0x3f * (0x3f - (pan[chn] - 0x3f));
+ else if (pan[chn] < 0x3f) /* pan left; so we scale the right down.*/
+ volume_R = volume_R / 0x3f * (0x3f - (0x3f-pan[chn]));
+ }
+ inst = instr[chn];
+
+ synth_setpatch(op, adlib_sbi[inst]);
+ synth_setvolume_L(op, volume_L);
+ synth_setvolume_R(op, volume_R);
+ synth_setnote(op, note, pitch[chn]);
+
+ oper_chn[op] = chn;
+ oper_note[op] = note;
+ free_voices--;
+
+#ifdef DEBUG_ADLIB
+ printf("play voice %d (%d rem): C%02x N%02x V%02x/%02x-%02x P%02x (%02x/%02x)\n", op, free_voices, chn, note, velocity, volume_L, volume_R, inst,
+ adlib_reg_L[register_base[2]+register_offset[op]] & 0x3f,
+ adlib_reg_L[register_base[3]+register_offset[op]] & 0x3f);
+#endif
+
+ return 0;
+}
+
+static
+void adlibemu_update_pitch(int chn, int note, int newpitch)
+{
+ int i;
+ int matched = 0;
+
+ pitch[chn] = newpitch;
+
+ for (i=0;i<ADLIB_VOICES;i++) {
+ if (oper_chn[i] == chn)
+ {
+ matched++;
+ synth_setnote(i, oper_note[i], newpitch);
+ }
+ }
+
+// printf("Matched %d notes on channel %d.\n", matched, chn);
+}
+
+void test_adlib (void) {
+
+ int voice = 0;
+#if 0
+ guint8 data[] = { 0x25, 0x21, 0x48, 0x48, 0xf0, 0xf2, 0xf0, 0xa5, 0x00, 0x00, 0x06 };
+#else
+ guint8 *data = adlib_sbi[0x0a];
+#endif
+
+#if 1
+ opl_write(register_base[0]+register_offset[voice], data[0]);
+ opl_write(register_base[1]+register_offset[voice], data[1]);
+ opl_write(register_base[2]+register_offset[voice], data[2]);
+ opl_write(register_base[3]+register_offset[voice], data[3]);
+ opl_write(register_base[4]+register_offset[voice], data[4]);
+ opl_write(register_base[5]+register_offset[voice], data[5]);
+ opl_write(register_base[6]+register_offset[voice], data[6]);
+ opl_write(register_base[7]+register_offset[voice], data[7]);
+ opl_write(register_base[8]+register_offset[voice], data[8]);
+ opl_write(register_base[9]+register_offset[voice], data[9]);
+ opl_write(register_base[10]+register_offset[voice], data[10]);
+#else
+ synth_setpatch(voice, data);
+#endif
+
+#if 0
+ opl_write(0xA0 + voice, 0x57);
+ opl_write(0xB0 + voice, 0x2d);
+#else
+ synth_setvolume_L(voice, 0x50);
+ synth_setvolume_R(voice, 0x50);
+ synth_setnote(voice, 0x30, 0);
+#endif
+
+ /*
+ instr[0x0e] = 0x0a;
+ instr[0x03] = 0x26;
+
+ adlibemu_start_note(0x0e, 0x30, 0x40);
+ sleep(1);
+ adlibemu_start_note(0x03, 0x48, 0x40);
+ sleep(1);
+ */
+}
+
+
+/* count is # of FRAMES, not bytes.
+ We assume 16-bit stereo frames (ie 4 bytes)
+*/
+static void
+opl2_poll (sfx_softseq_t *self, byte *dest, int count)
+{
+ gint16 *buffer = (gint16 *) dest;
+ gint16 *ptr = buffer;
+
+ if (!ready) {
+ fprintf(stderr, "synth_mixer(): !ready \n");
+ exit(1);
+ }
+
+ if (!buffer) {
+ fprintf(stderr, "synth_mixer(): !buffer \n");
+ exit(1);
+ }
+
+#if 0
+ {
+ static unsigned long remaining_delta = 0;
+ int samples;
+ int remaining = count;
+
+ while (remaining > 0) {
+ samples = remaining_delta * pcmout_sample_rate / 1000000;
+ samples = sci_min(samples, remaining);
+ if (samples) {
+ YM3812UpdateOne(ADLIB_LEFT, ptr, samples, 1);
+ YM3812UpdateOne(ADLIB_RIGHT, ptr+1, samples, 1);
+ }
+ if (remaining > samples) {
+ remaining_delta = (remaining - samples) * 1000000 / pcmout_sample_rate;
+ } else {
+ song->play_next_note();
+ remaining_delta = song->get_next_delta();
+ song->advance();
+ }
+ remaining -= samples;
+ }
+ }
+#endif
+
+ if (pcmout_stereo) {
+ YM3812UpdateOne (ym3812_L, ptr, count, 1);
+ YM3812UpdateOne (ym3812_R, ptr+1, count, 1);
+ } else {
+ YM3812UpdateOne (ym3812_L, ptr, count, 0);
+ }
+}
+
+static int
+opl2_init(sfx_softseq_t *self, byte *data_ptr, int data_length, byte *data2_ptr,
+ int data2_length)
+{
+ int i;
+
+ /* load up the patch.003 file, parse out the instruments */
+ if (data_length < 1344) {
+ sciprintf ("[sfx:seq:opl2] Invalid patch.003: Expected %d, got %d\n", 1344, data_length);
+ return -1;
+ }
+
+ for (i = 0; i < 48; i++)
+ make_sbi((adlib_def *)(data_ptr+(28 * i)), adlib_sbi[i]);
+
+ if (data_length > 1344)
+ for (i = 48; i < 96; i++)
+ make_sbi((adlib_def *)(data_ptr+2+(28 * i)), adlib_sbi[i]);
+
+ OPLBuildTables(FMOPL_ENV_BITS_HQ, FMOPL_EG_ENT_HQ);
+
+ if (!(ym3812_L = OPLCreate (OPL_TYPE_YM3812, OPL_INTERNAL_FREQ, SAMPLE_RATE)) ||
+ !(ym3812_R = OPLCreate (OPL_TYPE_YM3812, OPL_INTERNAL_FREQ, SAMPLE_RATE))) {
+ sciprintf ("[sfx:seq:opl2] Failure: Emulator init failed!\n");
+ return SFX_ERROR;
+ }
+
+ ready = 1;
+
+ opl2_allstop(self);
+ return SFX_OK;
+}
+
+
+static void
+opl2_exit(sfx_softseq_t *self)
+{
+ FM_OPL *opl = ym3812_L;
+ ym3812_L = NULL;
+ OPLDestroy(opl);
+ opl = ym3812_R;
+ ym3812_R = NULL;
+ OPLDestroy(opl);
+
+ // XXX deregister with pcm layer.
+}
+
+static void
+opl2_allstop(sfx_softseq_t *self)
+{
+ // printf("AdlibEmu: Reset\n");
+ if (! ready)
+ return;
+
+ adlibemu_init_lists();
+
+ OPLResetChip (ym3812_L);
+ OPLResetChip (ym3812_R);
+
+ opl_write(0x01, 0x20);
+ opl_write(0xBD, 0xc0);
+
+#ifdef DEBUG_ADLIB
+ printf("ADLIB: reset complete\n");
+#endif
+ // test_adlib();
+}
+
+int midi_adlibemu_reverb(short param)
+{
+ printf("ADLIB: reverb NYI %04x \n", param);
+ return 0;
+}
+
+int midi_adlibemu_event(guint8 command, guint8 note, guint8 velocity, guint32 delta)
+{
+ guint8 channel, oper;
+
+ channel = command & 0x0f;
+ oper = command & 0xf0;
+
+ switch (oper) {
+ case 0x80:
+ return adlibemu_stop_note(channel, note, velocity);
+ case 0x90: /* noteon and noteoff */
+ return adlibemu_start_note(channel, note, velocity);
+ case 0xe0: /* Pitch bend */
+ {
+ int bend = (note & 0x7f) | ((velocity & 0x7f) << 7);
+// printf("Bend to %d\n", bend);
+ adlibemu_update_pitch(channel, note, bend);
+ }
+ case 0xb0: /* CC changes. */
+ switch (note) {
+ case 0x07:
+ vol[channel] = velocity;
+ break;
+ case 0x0a:
+ pan[channel] = velocity;
+ break;
+ case 0x4b:
+ break;
+ case 0x7b: { /* all notes off */
+ int i = 0;
+ for (i=0;i<ADLIB_VOICES;i++)
+ if (oper_chn[i] == channel)
+ adlibemu_stop_note(channel, oper_note[i], 0);
+ break;
+ }
+ default:
+ ; /* XXXX ignore everything else for now */
+ }
+ return 0;
+ case 0xd0: /* aftertouch */
+ /* XXX Aftertouch in the OPL thing? */
+ return 0;
+ default:
+ printf("ADLIB: Unknown event %02x\n", command);
+ return 0;
+ }
+
+ return 0;
+}
+
+int midi_adlibemu_event2(guint8 command, guint8 param, guint32 delta)
+{
+ guint8 channel;
+ guint8 oper;
+
+ channel = command & 0x0f;
+ oper = command & 0xf0;
+ switch (oper) {
+ case 0xc0: /* change instrument */
+#ifdef DEBUG_ADLIB
+ printf("ADLIB: Selecting instrument %d on channel %d\n", param, channel);
+#endif
+ instr[channel] = param;
+ return 0;
+ default:
+ printf("ADLIB: Unknown event %02x\n", command);
+ }
+
+ return 0;
+}
+
+static void
+opl2_volume(sfx_softseq_t *self, int volume)
+{
+ guint8 i;
+
+ i = (guint8)volume * 15 / 100;
+
+ adlib_master=i;
+
+#ifdef DEBUG_ADLIB
+ printf("ADLIB: master volume set to %d\n", adlib_master);
+#endif
+}
+
+int
+opl2_set_option(sfx_softseq_t *self, char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+void
+opl2_event(sfx_softseq_t *self, byte cmd, int argc, byte *argv)
+{
+ if (argc == 1)
+ midi_adlibemu_event2(cmd, argv[0], 0);
+ else if (argc == 2)
+ midi_adlibemu_event(cmd, argv[0], argv[1], 0);
+}
+
+/* the driver struct */
+
+sfx_softseq_t sfx_softseq_opl2 = {
+ "adlibemu",
+ "0.1",
+ opl2_set_option,
+ opl2_init,
+ opl2_exit,
+ opl2_volume,
+ opl2_event,
+ opl2_poll,
+ opl2_allstop,
+ NULL,
+ 3, /* use patch.003 */
+ SFX_SEQ_PATCHFILE_NONE,
+ 0x4, /* Play flags */
+ 0, /* No rhythm channel (9) */
+ ADLIB_VOICES, /* # of voices */
+ {SAMPLE_RATE, CHANNELS, SFX_PCM_FORMAT_S16_NATIVE}
+};
diff --git a/engines/sci/sfx/softseq/pcspeaker.c b/engines/sci/sfx/softseq/pcspeaker.c
new file mode 100644
index 0000000000..771c8f0da5
--- /dev/null
+++ b/engines/sci/sfx/softseq/pcspeaker.c
@@ -0,0 +1,184 @@
+/***************************************************************************
+ soft-pcspeaker.c Copyright (C) 2004 Christoph Reichenbach
+
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public Licence as
+ published by the Free Software Foundaton; either version 2 of the
+ Licence, or (at your option) any later version.
+
+ It is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ merchantibility or fitness for a particular purpose. See the
+ GNU General Public Licence for more details.
+
+ You should have received a copy of the GNU General Public Licence
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+
+ Please contact the maintainer for any program-related bug reports or
+ inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+/* PC speaker software sequencer for FreeSCI */
+
+#include "../softseq.h"
+#include <sci_midi.h>
+
+#define FREQUENCY 94020
+
+static int volume = 0x0600;
+static int note = 0; /* Current halftone, or 0 if off */
+static int freq_count = 0;
+
+extern sfx_softseq_t sfx_softseq_pcspeaker;
+/* Forward-declare the sequencer we are defining here */
+
+
+static int
+sps_set_option(sfx_softseq_t *self, char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+static int
+sps_init(sfx_softseq_t *self, byte *patch, int patch_len, byte *patch2,
+ int patch2_len)
+{
+ return SFX_OK;
+}
+
+static void
+sps_exit(sfx_softseq_t *self)
+{
+}
+
+static void
+sps_event(sfx_softseq_t *self, byte command, int argc, byte *argv)
+{
+#if 0
+ fprintf(stderr, "Note [%02x : %02x %02x]\n", command, argc?argv[0] : 0, (argc > 1)? argv[1] : 0);
+#endif
+
+ switch (command & 0xf0) {
+
+ case 0x80:
+ if (argv[0] == note)
+ note = 0;
+ break;
+
+ case 0x90:
+ if (!argv[1]) {
+ if (argv[0] == note)
+ note = 0;
+ } else
+ note = argv[0];
+ /* Ignore velocity otherwise; we just use the global one */
+ break;
+
+ case 0xb0:
+ if (argv[1] == SCI_MIDI_CHANNEL_NOTES_OFF)
+ note = 0;
+ break;
+
+ default:
+#if DEBUG
+ fprintf(stderr, "[SFX:PCM-PC] Unused MIDI command %02x %02x %02x\n", command, argc?argv[0] : 0, (argc > 1)? argv[1] : 0);
+#endif
+ break; /* ignore */
+ }
+}
+
+#define BASE_NOTE 129 /* A10 */
+#define BASE_OCTAVE 10 /* A10, as I said */
+
+static int
+freq_table[12] = { /* A4 is 440Hz, halftone map is x |-> ** 2^(x/12) */
+ 28160, /* A10 */
+ 29834,
+ 31608,
+ 33488,
+ 35479,
+ 37589,
+ 39824,
+ 42192,
+ 44701,
+ 47359,
+ 50175,
+ 53159
+};
+
+
+void
+sps_poll(sfx_softseq_t *self, byte *dest, int len)
+{
+ int halftone_delta = note - BASE_NOTE;
+ int oct_diff = ((halftone_delta + BASE_OCTAVE * 12) / 12) - BASE_OCTAVE;
+ int halftone_index = (halftone_delta + (12*100)) % 12 ;
+ int freq = (!note)? 0 : freq_table[halftone_index] / (1 << (-oct_diff));
+ gint16 *buf = (gint16 *) dest;
+
+ int i;
+ for (i = 0; i < len; i++) {
+ if (note) {
+ freq_count += freq;
+ while (freq_count >= (FREQUENCY << 1))
+ freq_count -= (FREQUENCY << 1);
+
+ if (freq_count - freq < 0) {
+ /* Unclean rising edge */
+ int l = volume << 1;
+ buf[i] = -volume + (l*freq_count)/freq;
+ } else if (freq_count >= FREQUENCY
+ && freq_count - freq < FREQUENCY) {
+ /* Unclean falling edge */
+ int l = volume << 1;
+ buf[i] = volume - (l*(freq_count - FREQUENCY))/freq;
+ } else {
+ if (freq_count < FREQUENCY)
+ buf[i] = volume;
+ else
+ buf[i] = -volume;
+ }
+ } else
+ buf[i] = 0;
+ }
+
+}
+
+void
+sps_volume(sfx_softseq_t *self, int new_volume)
+{
+ volume = new_volume << 4;
+}
+
+void
+sps_allstop(sfx_softseq_t *self)
+{
+ note = 0;
+}
+
+sfx_softseq_t sfx_softseq_pcspeaker = {
+ "pc-speaker",
+ "0.3",
+ sps_set_option,
+ sps_init,
+ sps_exit,
+ sps_volume,
+ sps_event,
+ sps_poll,
+ sps_allstop,
+ NULL,
+ SFX_SEQ_PATCHFILE_NONE,
+ SFX_SEQ_PATCHFILE_NONE,
+ 0x20, /* PC speaker channel only */
+ 0, /* No rhythm channel */
+ 1, /* # of voices */
+ {FREQUENCY, SFX_PCM_MONO, SFX_PCM_FORMAT_S16_NATIVE}
+};
diff --git a/engines/sci/sfx/softseq/softsequencers.c b/engines/sci/sfx/softseq/softsequencers.c
new file mode 100644
index 0000000000..d0f544190e
--- /dev/null
+++ b/engines/sci/sfx/softseq/softsequencers.c
@@ -0,0 +1,71 @@
+/***************************************************************************
+ softsequencers.c Copyright (C) 2004 Christoph Reichenbach
+
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public Licence as
+ published by the Free Software Foundaton; either version 2 of the
+ Licence, or (at your option) any later version.
+
+ It is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ merchantibility or fitness for a particular purpose. See the
+ GNU General Public Licence for more details.
+
+ You should have received a copy of the GNU General Public Licence
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+
+ Please contact the maintainer for any program-related bug reports or
+ inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "../softseq.h"
+
+extern sfx_softseq_t sfx_softseq_opl2;
+extern sfx_softseq_t sfx_softseq_SN76496;
+extern sfx_softseq_t sfx_softseq_pcspeaker;
+extern sfx_softseq_t sfx_softseq_amiga;
+extern sfx_softseq_t sfx_softseq_mt32;
+extern sfx_softseq_t sfx_softseq_fluidsynth;
+
+static sfx_softseq_t *sw_sequencers[] = {
+ &sfx_softseq_opl2,
+/* &sfx_softseq_mt32, */
+ &sfx_softseq_SN76496,
+ &sfx_softseq_pcspeaker,
+ &sfx_softseq_amiga,
+#ifdef HAVE_FLUIDSYNTH_H
+ &sfx_softseq_fluidsynth,
+#endif
+ NULL
+};
+
+
+sfx_softseq_t *
+sfx_find_softseq(char *name)
+{
+ if (!name)
+ return sw_sequencers[0];
+ else {
+ int i = 0;
+ while (sw_sequencers[i])
+ if (!strcmp(sw_sequencers[i]->name, name))
+ return sw_sequencers[i];
+ else
+ ++i;
+
+ return NULL; /* Not found */
+ }
+}
diff --git a/engines/sci/sfx/songlib.c b/engines/sci/sfx/songlib.c
new file mode 100644
index 0000000000..36287845f0
--- /dev/null
+++ b/engines/sci/sfx/songlib.c
@@ -0,0 +1,282 @@
+/***************************************************************************
+ songlib.c Copyright (C) 2002 Christoph Reichenbach
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CJR) <reichenb@colorado.edu>
+
+***************************************************************************/
+
+#include <stdio.h>
+#include <sfx_engine.h>
+#include <sci_memory.h>
+
+#define debug_stream stderr
+
+GTimeVal
+song_sleep_time(GTimeVal *lastslept, long ticks)
+{
+ GTimeVal tv;
+ long timetosleep;
+ long timeslept; /* Time already slept */
+ timetosleep = ticks * SOUND_TICK; /* Time to sleep in us */
+
+ sci_get_current_time(&tv);
+ timeslept = 1000000 * (tv.tv_sec - lastslept->tv_sec) +
+ tv.tv_usec - lastslept->tv_usec;
+
+ timetosleep -= timeslept;
+
+ if (timetosleep < 0)
+ timetosleep = 0;
+
+ tv.tv_sec = timetosleep / 1000000;
+ tv.tv_usec = timetosleep % 1000000;
+
+ return tv;
+}
+
+
+GTimeVal
+song_next_wakeup_time(GTimeVal *lastslept, long ticks)
+{
+ GTimeVal retval;
+
+ retval.tv_sec = lastslept->tv_sec + (ticks / 60);
+ retval.tv_usec = lastslept->tv_usec + ((ticks % 60) * SOUND_TICK);
+
+ if (retval.tv_usec >= 1000000) {
+ retval.tv_usec -= 1000000;
+ ++retval.tv_sec;
+ }
+
+ return retval;
+}
+
+
+song_t *
+song_new(song_handle_t handle, song_iterator_t *it, int priority)
+{
+ song_t *retval;
+ retval = (song_t*) sci_malloc(sizeof(song_t));
+
+#ifdef SATISFY_PURIFY
+ memset(retval, 0, sizeof(song_t));
+#endif
+
+ retval->handle = handle;
+ retval->priority = priority;
+ retval->next = NULL;
+ retval->delay = 0;
+ retval->it = it;
+ retval->status = SOUND_STATUS_STOPPED;
+ retval->next_playing = NULL;
+ retval->next_stopping = NULL;
+ retval->restore_behavior = RESTORE_BEHAVIOR_CONTINUE;
+ retval->restore_time = 0;
+
+ return retval;
+}
+
+
+void
+song_lib_add(songlib_t songlib, song_t *song)
+{
+ song_t **seeker = NULL;
+ int pri = song->priority;
+
+ if (NULL == song) {
+ sciprintf("song_lib_add(): NULL passed for song\n");
+ return;
+ }
+
+ if (*(songlib.lib) == NULL) {
+ *(songlib.lib) = song;
+ song->next = NULL;
+
+ return;
+ }
+
+ seeker = (songlib.lib);
+ while (*seeker && ((*seeker)->priority > pri))
+ seeker = &((*seeker)->next);
+
+ song->next = *seeker;
+ *seeker = song;
+}
+
+static void /* Recursively free a chain of songs */
+_songfree_chain(song_t *song)
+{
+ if (song) {
+ _songfree_chain(song->next);
+ songit_free(song->it);
+ song->it = NULL;
+ free(song);
+ }
+}
+
+
+void
+song_lib_init(songlib_t *songlib)
+{
+ songlib->lib = &(songlib->_s);
+ songlib->_s = NULL;
+}
+
+void
+song_lib_free(songlib_t songlib)
+{
+ _songfree_chain(*(songlib.lib));
+ *(songlib.lib) = NULL;
+}
+
+
+song_t *
+song_lib_find(songlib_t songlib, song_handle_t handle)
+{
+ song_t *seeker = *(songlib.lib);
+
+ while (seeker) {
+ if (seeker->handle == handle)
+ break;
+ seeker = seeker->next;
+ }
+
+ return seeker;
+}
+
+
+song_t *
+song_lib_find_next_active(songlib_t songlib, song_t *other)
+{
+ song_t *seeker = other? other->next : *(songlib.lib);
+
+ while (seeker) {
+ if ((seeker->status == SOUND_STATUS_WAITING) ||
+ (seeker->status == SOUND_STATUS_PLAYING))
+ break;
+ seeker = seeker->next;
+ }
+
+ /* Only return songs that have equal priority */
+ if (other && seeker && other->priority > seeker->priority)
+ return NULL;
+
+ return seeker;
+}
+
+song_t *
+song_lib_find_active(songlib_t songlib)
+{
+ return song_lib_find_next_active(songlib, NULL);
+}
+
+int
+song_lib_remove(songlib_t songlib, song_handle_t handle)
+{
+ int retval;
+ song_t *goner = *(songlib.lib);
+
+ if (!goner)
+ return -1;
+
+ if (goner->handle == handle)
+ *(songlib.lib) = goner->next;
+
+ else {
+ while ((goner->next) && (goner->next->handle != handle))
+ goner = goner->next;
+
+ if (goner->next) { /* Found him? */
+ song_t *oldnext = goner->next;
+
+ goner->next = goner->next->next;
+ goner = oldnext;
+ } else return -1; /* No. */
+ }
+
+ retval = goner->status;
+
+ songit_free(goner->it);
+ free(goner);
+
+ return retval;
+}
+
+void
+song_lib_resort(songlib_t songlib, song_t *song)
+{
+ if (*(songlib.lib) == song)
+ *(songlib.lib) = song->next;
+ else {
+ song_t *seeker = *(songlib.lib);
+
+ while (seeker->next && (seeker->next != song))
+ seeker = seeker->next;
+
+ if (seeker->next)
+ seeker->next = seeker->next->next;
+ }
+
+ song_lib_add(songlib, song);
+}
+
+int
+song_lib_count(songlib_t songlib)
+{
+ song_t *seeker = *(songlib.lib);
+ int retval = 0;
+
+ while (seeker) {
+ retval++;
+ seeker = seeker->next;
+ }
+
+ return retval;
+}
+
+void
+song_lib_set_restore_behavior(songlib_t songlib, song_handle_t handle, RESTORE_BEHAVIOR action)
+{
+ song_t *seeker = song_lib_find(songlib, handle);
+
+ seeker->restore_behavior = action;
+}
+
+void
+song_lib_dump(songlib_t songlib, int line)
+{
+ song_t *seeker = *(songlib.lib);
+
+ fprintf(debug_stream,"L%d:", line);
+ do {
+ fprintf(debug_stream," %p", (void *)seeker);
+
+ if (seeker) {
+ fprintf(debug_stream,"[%04lx,p=%d,s=%d]->", seeker->handle, seeker->priority,seeker->status);
+ seeker = seeker->next;
+ }
+ fprintf(debug_stream,"\n");
+ } while (seeker);
+ fprintf(debug_stream,"\n");
+
+}
+
diff --git a/engines/sci/sfx/test-iterator.c b/engines/sci/sfx/test-iterator.c
new file mode 100644
index 0000000000..1ecce673f9
--- /dev/null
+++ b/engines/sci/sfx/test-iterator.c
@@ -0,0 +1,450 @@
+/***************************************************************************
+ Copyright (C) 2008 Christoph Reichenbach
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantability,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+
+#include <sfx_iterator.h>
+#include "sfx_iterator_internal.h"
+#include <stdarg.h>
+#include <stdio.h>
+
+#define ASSERT_S(x) if (!(x)) { error("Failed assertion in L%d: " #x "\n", __LINE__); return; }
+#define ASSERT(x) ASSERT_S(x)
+
+/* Tests the song iterators */
+
+int errors = 0;
+
+void
+error(char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "[ERROR] ");
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ ++errors;
+}
+
+
+/* The simple iterator will finish after a fixed amount of time. Before that,
+** it emits (absolute) cues in ascending order. */
+struct simple_it_struct {
+ INHERITS_SONG_ITERATOR;
+ int lifetime_remaining;
+ char *cues;
+ int cue_counter;
+ int cue_progress;
+ int cues_nr;
+} simple_iterator;
+
+int
+simple_it_next(song_iterator_t *_self, unsigned char *buf, int *result)
+{
+ struct simple_it_struct *self = (struct simple_it_struct *) _self;
+
+ if (self->lifetime_remaining == -1) {
+ error("Song iterator called post mortem");
+ return SI_FINISHED;
+ }
+
+ if (self->lifetime_remaining) {
+
+ if (self->cue_counter < self->cues_nr) {
+ int time_to_cue = self->cues[self->cue_counter];
+
+ if (self->cue_progress == time_to_cue) {
+ ++self->cue_counter;
+ self->cue_progress = 0;
+ *result = self->cue_counter;
+ return SI_ABSOLUTE_CUE;
+ } else {
+ int retval = time_to_cue - self->cue_progress;
+ self->cue_progress = time_to_cue;
+
+ if (retval > self->lifetime_remaining) {
+ retval = self->lifetime_remaining;
+ self->lifetime_remaining = 0;
+ self->cue_progress = retval;
+ return retval;
+ }
+
+ self->lifetime_remaining -= retval;
+ return retval;
+ }
+ } else {
+ int retval = self->lifetime_remaining;
+ self->lifetime_remaining = 0;
+ return retval;
+ }
+
+ } else {
+ self->lifetime_remaining = -1;
+ return SI_FINISHED;
+ }
+}
+
+sfx_pcm_feed_t *
+simple_it_pcm_feed(song_iterator_t *_self)
+{
+ error("No PCM feed!\n");
+ return NULL;
+}
+
+void
+simple_it_init(song_iterator_t *_self)
+{
+}
+
+song_iterator_t *
+simple_it_handle_message(song_iterator_t *_self, song_iterator_message_t msg)
+{
+ return NULL;
+}
+
+void
+simple_it_cleanup(song_iterator_t *_self)
+{
+}
+
+/* Initialises the simple iterator.
+** Parameters: (int) delay: Number of ticks until the iterator finishes
+** (int *) cues: An array of cue delays (cue values are [1,2...])
+** (int) cues_nr: Number of cues in ``cues''
+** The first cue is emitted after cues[0] ticks, and it is 1. After cues[1] additional ticks
+** the next cue is emitted, and so on. */
+song_iterator_t *
+setup_simple_iterator(int delay, char *cues, int cues_nr)
+{
+ simple_iterator.lifetime_remaining = delay;
+ simple_iterator.cues = cues;
+ simple_iterator.cue_counter = 0;
+ simple_iterator.cues_nr = cues_nr;
+ simple_iterator.cue_progress = 0;
+
+ simple_iterator.ID = 42;
+ simple_iterator.channel_mask = 0x004f;
+ simple_iterator.flags = 0;
+ simple_iterator.priority = 1;
+
+ simple_iterator.death_listeners_nr = 0;
+
+ simple_iterator.cleanup = simple_it_cleanup;
+ simple_iterator.init = simple_it_init;
+ simple_iterator.handle_message = simple_it_handle_message;
+ simple_iterator.get_pcm_feed = simple_it_pcm_feed;
+ simple_iterator.next = simple_it_next;
+
+ return (song_iterator_t *) &simple_iterator;
+}
+
+#define ASSERT_SIT ASSERT(it == simple_it)
+#define ASSERT_FFIT ASSERT(it == ff_it)
+#define ASSERT_NEXT(n) ASSERT(songit_next(&it, data, &result, IT_READER_MASK_ALL) == n)
+#define ASSERT_RESULT(n) ASSERT(result == n)
+#define ASSERT_CUE(n) ASSERT_NEXT(SI_ABSOLUTE_CUE); ASSERT_RESULT(n)
+
+void
+test_simple_it()
+{
+ song_iterator_t *it;
+ song_iterator_t *simple_it = (song_iterator_t *) &simple_iterator;
+ unsigned char data[4];
+ int result;
+ puts("[TEST] simple iterator (test artifact)");
+
+ it = setup_simple_iterator(42, NULL, 0);
+
+ ASSERT_SIT;
+ ASSERT_NEXT(42);
+ ASSERT_SIT;
+ ASSERT_NEXT(SI_FINISHED);
+ ASSERT_SIT;
+
+ it = setup_simple_iterator(42, "\003\004", 2);
+ ASSERT_SIT;
+ ASSERT_NEXT(3);
+ ASSERT_CUE(1);
+ ASSERT_SIT;
+ ASSERT_NEXT(4);
+ ASSERT_CUE(2);
+ ASSERT_SIT;
+// fprintf(stderr, "XXX => %d\n", songit_next(&it, data, &result, IT_READER_MASK_ALL));
+ ASSERT_NEXT(35);
+ ASSERT_NEXT(SI_FINISHED);
+ ASSERT_SIT;
+
+ puts("[TEST] Test OK.");
+}
+
+void
+test_fastforward()
+{
+ song_iterator_t *it;
+ song_iterator_t *simple_it = (song_iterator_t *) &simple_iterator;
+ song_iterator_t *ff_it;
+ unsigned char data[4];
+ int result;
+ puts("[TEST] fast-forward iterator");
+
+ it = setup_simple_iterator(42, NULL, 0);
+ ff_it = it = new_fast_forward_iterator(it, 0);
+ ASSERT_FFIT;
+ ASSERT_NEXT(42);
+ ASSERT_SIT; /* Must have morphed back */
+ ASSERT_NEXT(SI_FINISHED);
+ ASSERT_SIT;
+
+ it = setup_simple_iterator(42, NULL, 0);
+ ff_it = it = new_fast_forward_iterator(it, 1);
+ ASSERT_FFIT;
+ ASSERT_NEXT(41);
+ /* May or may not have morphed back here */
+ ASSERT_NEXT(SI_FINISHED);
+ ASSERT_SIT;
+
+ it = setup_simple_iterator(42, NULL, 0);
+ ff_it = it = new_fast_forward_iterator(it, 41);
+ ASSERT_FFIT;
+ ASSERT_NEXT(1);
+ /* May or may not have morphed back here */
+ ASSERT_NEXT(SI_FINISHED);
+ ASSERT_SIT;
+
+ it = setup_simple_iterator(42, NULL, 0);
+ ff_it = it = new_fast_forward_iterator(it, 42);
+ ASSERT_NEXT(SI_FINISHED);
+ /* May or may not have morphed back here */
+
+ it = setup_simple_iterator(42, NULL, 0);
+ ff_it = it = new_fast_forward_iterator(it, 10000);
+ ASSERT_NEXT(SI_FINISHED);
+ /* May or may not have morphed back here */
+
+ it = setup_simple_iterator(42, "\003\004", 2);
+ ff_it = it = new_fast_forward_iterator(it, 2);
+ ASSERT_FFIT;
+ ASSERT_NEXT(1);
+ ASSERT_CUE(1);
+ ASSERT_SIT;
+ ASSERT_NEXT(4);
+ ASSERT_CUE(2);
+ ASSERT_SIT;
+ ASSERT_NEXT(35);
+ ASSERT_NEXT(SI_FINISHED);
+ ASSERT_SIT;
+
+ it = setup_simple_iterator(42, "\003\004", 2);
+ ff_it = it = new_fast_forward_iterator(it, 5);
+ ASSERT_FFIT;
+ ASSERT_CUE(1);
+ ASSERT_FFIT;
+ ASSERT_NEXT(2);
+ ASSERT_CUE(2);
+ ASSERT_SIT;
+ ASSERT_NEXT(35);
+ ASSERT_NEXT(SI_FINISHED);
+ ASSERT_SIT;
+
+ it = setup_simple_iterator(42, "\003\004", 2);
+ ff_it = it = new_fast_forward_iterator(it, 41);
+ ASSERT_FFIT;
+ ASSERT_CUE(1);
+ ASSERT_FFIT;
+ ASSERT_CUE(2);
+ ASSERT_FFIT;
+ ASSERT_NEXT(1);
+ ASSERT_NEXT(SI_FINISHED);
+ ASSERT_SIT;
+
+ puts("[TEST] Test OK.");
+}
+
+#define SIMPLE_SONG_SIZE 50
+
+static unsigned char simple_song[SIMPLE_SONG_SIZE] = {
+ 0x00, /* Regular song */
+ /* Only use channel 0 for all devices */
+ 0x02, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* Song begins here */
+ 42, 0x90, 60, 0x7f, /* Play C after 42 ticks */
+ 02, 64, 0x42, /* Play E after 2 more ticks, using running status mode */
+ 0xf8, 10, 0x80, 60, 0x02, /* Stop C after 250 ticks */
+ 0, 64, 0x00, /* Stop E immediately */
+ 00, 0xfc /* Stop song */
+};
+
+#define ASSERT_MIDI3(cmd, arg0, arg1) \
+ ASSERT(data[0] == cmd); \
+ ASSERT(data[1] == arg0); \
+ ASSERT(data[2] == arg1);
+
+void
+test_iterator_sci0()
+{
+ song_iterator_t *it = songit_new (simple_song, SIMPLE_SONG_SIZE, SCI_SONG_ITERATOR_TYPE_SCI0, 0l);
+ unsigned char data[4];
+ int result;
+ SIMSG_SEND(it, SIMSG_SET_PLAYMASK(0x0001)); /* Initialise song, enabling channel 0 */
+
+ puts("[TEST] SCI0-style song");
+ ASSERT_NEXT(42);
+ ASSERT_NEXT(0);
+ ASSERT_MIDI3(0x90, 60, 0x7f);
+ ASSERT_NEXT(2);
+ ASSERT_NEXT(0);
+ ASSERT_MIDI3(0x90, 64, 0x42);
+ ASSERT_NEXT(250);
+ ASSERT_NEXT(0);
+ ASSERT_MIDI3(0x80, 60, 0x02);
+ ASSERT_NEXT(0);
+ ASSERT_MIDI3(0x80, 64, 0x00);
+ ASSERT_NEXT(SI_FINISHED);
+ puts("[TEST] Test OK.");
+}
+
+
+
+void
+test_iterator_sci0_loop()
+{
+ song_iterator_t *it = songit_new (simple_song, SIMPLE_SONG_SIZE, SCI_SONG_ITERATOR_TYPE_SCI0, 0l);
+ unsigned char data[4];
+ int result;
+ SIMSG_SEND(it, SIMSG_SET_PLAYMASK(0x0001)); /* Initialise song, enabling channel 0 */
+ SIMSG_SEND(it, SIMSG_SET_LOOPS(2)); /* Loop one additional time */
+
+ puts("[TEST] SCI0-style song with looping");
+ ASSERT_NEXT(42);
+ ASSERT_NEXT(0);
+ ASSERT_MIDI3(0x90, 60, 0x7f);
+ ASSERT_NEXT(2);
+ ASSERT_NEXT(0);
+ ASSERT_MIDI3(0x90, 64, 0x42);
+ ASSERT_NEXT(250);
+ ASSERT_NEXT(0);
+ ASSERT_MIDI3(0x80, 60, 0x02);
+ ASSERT_NEXT(0);
+ ASSERT_MIDI3(0x80, 64, 0x00);
+ ASSERT_NEXT(SI_LOOP);
+ ASSERT_NEXT(42);
+ ASSERT_NEXT(0);
+ ASSERT_MIDI3(0x90, 60, 0x7f);
+ ASSERT_NEXT(2);
+ ASSERT_NEXT(0);
+ ASSERT_MIDI3(0x90, 64, 0x42);
+ ASSERT_NEXT(250);
+ ASSERT_NEXT(0);
+ ASSERT_MIDI3(0x80, 60, 0x02);
+ ASSERT_NEXT(0);
+ ASSERT_MIDI3(0x80, 64, 0x00);
+ ASSERT_NEXT(SI_FINISHED);
+ puts("[TEST] Test OK.");
+}
+
+
+
+#define LOOP_SONG_SIZE 54
+
+unsigned char loop_song[LOOP_SONG_SIZE] = {
+ 0x00, /* Regular song song */
+ /* Only use channel 0 for all devices */
+ 0x02, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* Song begins here */
+ 42, 0x90, 60, 0x7f, /* Play C after 42 ticks */
+ 13, 0x80, 60, 0x00, /* Stop C after 13 ticks */
+ 00, 0xCF, 0x7f, /* Set loop point */
+ 02, 0x90, 64, 0x42, /* Play E after 2 more ticks, using running status mode */
+ 03, 0x80, 64, 0x00, /* Stop E after 3 ticks */
+ 00, 0xfc /* Stop song/loop */
+};
+
+
+void
+test_iterator_sci0_mark_loop()
+{
+ song_iterator_t *it = songit_new (loop_song, LOOP_SONG_SIZE, SCI_SONG_ITERATOR_TYPE_SCI0, 0l);
+ unsigned char data[4];
+ int result;
+ SIMSG_SEND(it, SIMSG_SET_PLAYMASK(0x0001)); /* Initialise song, enabling channel 0 */
+ SIMSG_SEND(it, SIMSG_SET_LOOPS(3)); /* Loop once more */
+
+ puts("[TEST] SCI0-style song with loop mark, looping");
+ ASSERT_NEXT(42);
+ ASSERT_NEXT(0);
+ ASSERT_MIDI3(0x90, 60, 0x7f);
+ ASSERT_NEXT(13);
+ ASSERT_NEXT(0);
+ ASSERT_MIDI3(0x80, 60, 0x00);
+ /* Loop point here: we don't observe that in the iterator interface yet, though */
+ ASSERT_NEXT(2);
+ ASSERT_NEXT(0);
+ ASSERT_MIDI3(0x90, 64, 0x42);
+ ASSERT_NEXT(3);
+ ASSERT_NEXT(0);
+ ASSERT_MIDI3(0x80, 64, 0x00);
+ /* Now we loop back to the loop pont */
+ ASSERT_NEXT(SI_LOOP);
+ ASSERT_NEXT(2);
+ ASSERT_NEXT(0);
+ ASSERT_MIDI3(0x90, 64, 0x42);
+ ASSERT_NEXT(3);
+ ASSERT_NEXT(0);
+ ASSERT_MIDI3(0x80, 64, 0x00);
+ /* ...and one final time */
+ ASSERT_NEXT(SI_LOOP);
+ ASSERT_NEXT(2);
+ ASSERT_NEXT(0);
+ ASSERT_MIDI3(0x90, 64, 0x42);
+ ASSERT_NEXT(3);
+ ASSERT_NEXT(0);
+ ASSERT_MIDI3(0x80, 64, 0x00);
+
+ ASSERT_NEXT(SI_FINISHED);
+ puts("[TEST] Test OK.");
+}
+
+
+
+int
+main(int argc, char **argv)
+{
+ test_simple_it();
+ test_fastforward();
+ test_iterator_sci0();
+ test_iterator_sci0_loop();
+ test_iterator_sci0_mark_loop();
+ if (errors != 0)
+ fprintf(stderr, "[ERROR] %d errors total.\n", errors);
+ return (errors != 0);
+}
diff --git a/engines/sci/sfx/tests/stubs.c b/engines/sci/sfx/tests/stubs.c
new file mode 100644
index 0000000000..06da6f9419
--- /dev/null
+++ b/engines/sci/sfx/tests/stubs.c
@@ -0,0 +1,9 @@
+int sciprintf(char *fmt,...)
+{
+ return 0;
+}
+
+int sfx_pcm_available()
+{
+ return 1;
+} \ No newline at end of file
diff --git a/engines/sci/sfx/tests/tests.cpp b/engines/sci/sfx/tests/tests.cpp
new file mode 100644
index 0000000000..10eacc0de2
--- /dev/null
+++ b/engines/sci/sfx/tests/tests.cpp
@@ -0,0 +1,172 @@
+#include <stdio.h>
+#include <sfx_iterator.h>
+
+#define TESTEQUAL(x, y) if(x != y) printf("test failure: expected %04x, got %04x @ file %s line %d \n", x, y, __FILE__, __LINE__);
+
+static char calledDeathListenerCallback;
+
+static unsigned char song[] = {
+// PCM not present
+0,
+// channel defs
+0, 0x20, 0, 0x21, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0,
+// note on, wait, note off
+0xAA,
+0x90, 0xAA, 0xAA,
+0xAA,
+0x80, 0xAA, 0xAA,
+0xAA,
+0x91, 0xAA, 0xAA,
+0xAA,
+0x81, 0xAA, 0xAA,
+0xAA,
+// end track
+0xFC};
+
+#define SONG_CMD_COUNT 10
+
+#define TEST_SETUP() \
+ unsigned char cmds[4] = {0}; \
+ int result = 0; \
+ int message; \
+ int i; \
+\
+ song_iterator_t *it = songit_new(song, sizeof(song), 0, 0); \
+ it->init(it); \
+ SIMSG_SEND(it, SIMSG_SET_PLAYMASK(0x20));\
+ calledDeathListenerCallback = 0;
+
+#define TEST_TEARDOWN() \
+ songit_free(it);
+
+
+void testFinishSong()
+{
+ TEST_SETUP();
+ message = songit_next(&it, &cmds, &result, IT_READER_MASK_ALL);
+ TESTEQUAL(0xAA, message);
+
+ message = songit_next(&it, &cmds, &result, IT_READER_MASK_ALL);
+ TESTEQUAL(0, message);
+ TESTEQUAL(3, result);
+
+ for (i=0; i < SONG_CMD_COUNT - 2; i++)
+ {
+ message = songit_next(&it, &cmds, &result, IT_READER_MASK_ALL);
+ }
+
+ TESTEQUAL(SI_FINISHED, message);
+
+ TEST_TEARDOWN();
+}
+
+
+void DeathListenerCallback(void *v1, void *v2)
+{
+ calledDeathListenerCallback++;
+ return;
+}
+
+void testDeathListener()
+{
+ TEST_SETUP();
+
+ song_iterator_add_death_listener(
+ it,
+ it,
+ (void (*)(void *, void*))DeathListenerCallback);
+
+ for (i=0; i < SONG_CMD_COUNT; i++)
+ {
+ message = songit_next(&it, &cmds, &result, IT_READER_MASK_ALL);
+ }
+
+ TESTEQUAL(SI_FINISHED, message);
+
+ TEST_TEARDOWN();
+
+ TESTEQUAL(1, calledDeathListenerCallback);
+}
+
+void testMultipleDeathListeners()
+{
+ TEST_SETUP();
+
+ song_iterator_add_death_listener(
+ it,
+ it,
+ (void (*)(void *, void*))DeathListenerCallback);
+
+ song_iterator_add_death_listener(
+ it,
+ it,
+ (void (*)(void *, void*))DeathListenerCallback);
+
+ for (i=0; i < SONG_CMD_COUNT; i++)
+ {
+ message = songit_next(&it, &cmds, &result, IT_READER_MASK_ALL);
+ }
+
+ TESTEQUAL(SI_FINISHED, message);
+
+ TEST_TEARDOWN();
+
+ TESTEQUAL(2, calledDeathListenerCallback);
+}
+
+void testStopSong()
+{
+ TEST_SETUP();
+ SIMSG_SEND(it, SIMSG_STOP);
+
+ message = songit_next(&it, &cmds, &result, IT_READER_MASK_ALL);
+ TESTEQUAL(SI_FINISHED, message);
+
+ TEST_TEARDOWN();
+}
+
+void testStopLoopedSong()
+{
+ TEST_SETUP();
+
+ SIMSG_SEND(it, SIMSG_SET_LOOPS(3));
+ message = songit_next(&it, &cmds, &result, IT_READER_MASK_ALL);
+ TESTEQUAL(0xAA, message);
+
+ SIMSG_SEND(it, SIMSG_STOP);
+ message = songit_next(&it, &cmds, &result, IT_READER_MASK_ALL);
+ TESTEQUAL(SI_FINISHED, message);
+
+ TEST_TEARDOWN();
+}
+
+void testChangeSongMask()
+{
+ TEST_SETUP();
+
+ message = songit_next(&it, &cmds, &result, IT_READER_MASK_ALL);
+ TESTEQUAL(0xAA, message);
+
+ SIMSG_SEND(it, SIMSG_SET_PLAYMASK(0x40));
+ message = songit_next(&it, &cmds, &result, IT_READER_MASK_ALL);
+ TESTEQUAL(0, message);
+ TESTEQUAL(0, result);
+
+ TEST_TEARDOWN();
+}
+
+
+int main(int argc, char* argv[])
+{
+ testFinishSong();
+ testDeathListener();
+ testMultipleDeathListeners();
+ testStopSong();
+ testStopLoopedSong();
+ testChangeSongMask();
+ return 0;
+}
+
diff --git a/engines/sci/sfx/tests/tests.vcproj b/engines/sci/sfx/tests/tests.vcproj
new file mode 100644
index 0000000000..a50c794bce
--- /dev/null
+++ b/engines/sci/sfx/tests/tests.vcproj
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.10"
+ Name="tests"
+ ProjectGUID="{3C6F6B16-D092-495C-B611-03FFDBFF27FC}"
+ Keyword="Win32Proj">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="Debug"
+ IntermediateDirectory="Debug"
+ ConfigurationType="1"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\include;..\..\include\win32"
+ PreprocessorDefinitions="PACKAGE=\&quot;freesci\&quot;;HAVE_GETOPT_H;HAVE_LIBPNG;VERSION=__TIMESTAMP__;_DEBUG;WIN32;_CONSOLE;HAVE_STRING_H;HAVE_MEMCHR"
+ MinimalRebuild="TRUE"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="5"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="4"
+ CompileAs="1"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="winmm.lib"
+ OutputFile="$(OutDir)/tests.exe"
+ LinkIncremental="2"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile="$(OutDir)/tests.pdb"
+ SubSystem="1"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="Release"
+ IntermediateDirectory="Release"
+ ConfigurationType="1"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\..\include"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ RuntimeLibrary="4"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="TRUE"
+ DebugInformationFormat="3"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="winmm.lib"
+ OutputFile="$(OutDir)/tests.exe"
+ LinkIncremental="1"
+ GenerateDebugInformation="TRUE"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
+ <File
+ RelativePath="..\..\sfx\iterator.c">
+ </File>
+ <File
+ RelativePath="..\..\sfx\pcm-iterator.c">
+ </File>
+ <File
+ RelativePath="..\..\scicore\sci_memory.c">
+ </File>
+ <File
+ RelativePath=".\stubs.c">
+ </File>
+ <File
+ RelativePath=".\tests.cpp">
+ </File>
+ <File
+ RelativePath="..\..\scicore\tools.c">
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
+ <File
+ RelativePath="..\..\include\win32\sci_win32.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
+ </Filter>
+ <File
+ RelativePath=".\ReadMe.txt">
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/engines/sci/sfx/time.c b/engines/sci/sfx/time.c
new file mode 100644
index 0000000000..553eb406f8
--- /dev/null
+++ b/engines/sci/sfx/time.c
@@ -0,0 +1,131 @@
+/***************************************************************************
+ time.c Copyright (C) 2003 Christoph Reichenbach
+
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public Licence as
+ published by the Free Software Foundaton; either version 2 of the
+ Licence, or (at your option) any later version.
+
+ It is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ merchantibility or fitness for a particular purpose. See the
+ GNU General Public Licence for more details.
+
+ You should have received a copy of the GNU General Public Licence
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+
+ Please contact the maintainer for any program-related bug reports or
+ inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+
+#include <sfx_time.h>
+#include <resource.h>
+
+sfx_timestamp_t
+sfx_new_timestamp(long secs, long usecs, int frame_rate)
+{
+ sfx_timestamp_t r;
+ r.secs = secs;
+ r.usecs = usecs;
+ r.frame_rate = frame_rate;
+ r.frame_offset = 0;
+
+ return r;
+}
+
+
+sfx_timestamp_t
+sfx_timestamp_add(sfx_timestamp_t timestamp, int frames)
+{
+ timestamp.frame_offset += frames;
+
+ if (timestamp.frame_offset < 0) {
+ int secsub = 1 + (-timestamp.frame_offset / timestamp.frame_rate);
+
+ timestamp.frame_offset += timestamp.frame_rate * secsub;
+ timestamp.secs -= secsub;
+ }
+
+ timestamp.secs += (timestamp.frame_offset / timestamp.frame_rate);
+ timestamp.frame_offset %= timestamp.frame_rate;
+
+ return timestamp;
+}
+
+int
+sfx_timestamp_frame_diff(sfx_timestamp_t a, sfx_timestamp_t b)
+{
+ long usecdelta = 0;
+
+ if (a.frame_rate != b.frame_rate) {
+ fprintf(stderr, "Fatal: The semantics of subtracting two timestamps with a different base from each other is not defined!\n");
+ BREAKPOINT();
+ }
+
+ if (a.usecs != b.usecs) {
+#if (SIZEOF_LONG >= 8)
+ usecdelta = (a.usecs * a.frame_rate) / 1000000
+ - (b.usecs * b.frame_rate) / 1000000;
+#else
+ usecdelta = ((a.usecs/1000) * a.frame_rate) / 1000
+ - ((b.usecs/1000) * b.frame_rate) / 1000;
+#endif
+ }
+
+ return usecdelta
+ + (a.secs - b.secs) * a.frame_rate
+ + a.frame_offset - b.frame_offset;
+}
+
+long
+sfx_timestamp_usecs_diff(sfx_timestamp_t t1, sfx_timestamp_t t2)
+{
+ long secs1, secs2;
+ long usecs1, usecs2;
+
+ sfx_timestamp_gettime(&t1, &secs1, &usecs1);
+ sfx_timestamp_gettime(&t2, &secs2, &usecs2);
+
+ return (usecs1 - usecs2) + ((secs1 - secs2) * 1000000);
+}
+
+sfx_timestamp_t
+sfx_timestamp_renormalise(sfx_timestamp_t timestamp, int new_freq)
+{
+ sfx_timestamp_t r;
+ sfx_timestamp_gettime(&timestamp, &r.secs, &r.usecs);
+ r.frame_rate = new_freq;
+ r.frame_offset = 0;
+
+ return r;
+}
+
+void
+sfx_timestamp_gettime(sfx_timestamp_t *timestamp, long *secs, long *usecs)
+{
+ long ust = timestamp->usecs;
+ /* On 64 bit machines, we can do an accurate computation */
+#if (SIZEOF_LONG >= 8)
+ ust += (timestamp->frame_offset * 1000000l) / (timestamp->frame_rate);
+#else
+ ust += (timestamp->frame_offset * 1000l) / (timestamp->frame_rate / 1000l);
+#endif
+
+ if (ust > 1000000) {
+ ust -= 1000000;
+ *secs = timestamp->secs + 1;
+ } else
+ *secs = timestamp->secs;
+
+ *usecs = ust;
+}
+
diff --git a/engines/sci/sfx/timer/Makefile.am b/engines/sci/sfx/timer/Makefile.am
new file mode 100644
index 0000000000..74f3bee426
--- /dev/null
+++ b/engines/sci/sfx/timer/Makefile.am
@@ -0,0 +1,4 @@
+EXTRA_DIST = pthread.c
+noinst_LIBRARIES = libscitimer.a
+INCLUDES = -I$(top_srcdir)/src/include @EXTRA_INCLUDES@
+libscitimer_a_SOURCES = timers.c sigalrm.c
diff --git a/engines/sci/sfx/timer/pthread.c b/engines/sci/sfx/timer/pthread.c
new file mode 100644
index 0000000000..074adbe08f
--- /dev/null
+++ b/engines/sci/sfx/timer/pthread.c
@@ -0,0 +1,104 @@
+/***************************************************************************
+ pthread.c Copyright (C) 2005 Walter van Niftrik
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Walter van Niftrik <w.f.b.w.v.niftrik@stud.tue.nl>
+
+***************************************************************************/
+
+#include <sfx_timer.h>
+#include <pthread.h>
+#include <unistd.h>
+
+/* Timer frequency in hertz */
+#define FREQ 60
+
+/* Delay in ms */
+#define DELAY (1000 / FREQ)
+
+static void (*callback)(void *) = NULL;
+static void *callback_data = NULL;
+pthread_t thread;
+volatile static int thread_run;
+
+static void *
+timer_thread(void *arg)
+{
+ while (thread_run) {
+ if (callback)
+ callback(callback_data);
+
+ usleep(DELAY * 1000);
+ }
+
+ return NULL;
+}
+
+static int
+set_option(char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+static int
+init(void (*func)(void *), void *data)
+{
+ if (callback) {
+ fprintf(stderr, "Error: Attempt to initialize pthread timer more than once\n");
+ return SFX_ERROR;
+ }
+
+ if (!func) {
+ fprintf(stderr, "Error: Attempt to initialize pthread timer w/o callback\n");
+ return SFX_ERROR;
+ }
+
+ callback = func;
+ callback_data = data;
+
+ thread_run = 1;
+ if (pthread_create(&thread, NULL, timer_thread, NULL)) {
+ fprintf(stderr, "Error: Could not create thread.\n");
+ return SFX_ERROR;
+ }
+
+ return SFX_OK;
+}
+
+static int
+stop(void)
+{
+ thread_run = 0;
+ pthread_join(thread, NULL);
+
+ return SFX_OK;
+}
+
+sfx_timer_t sfx_timer_pthread = {
+ "pthread",
+ "0.1",
+ DELAY,
+ 0,
+ &set_option,
+ &init,
+ &stop
+};
diff --git a/engines/sci/sfx/timer/scummvm.cpp b/engines/sci/sfx/timer/scummvm.cpp
new file mode 100644
index 0000000000..f4e08d441a
--- /dev/null
+++ b/engines/sci/sfx/timer/scummvm.cpp
@@ -0,0 +1,52 @@
+#include "common/timer.h"
+#include "engines/engine.h"
+#include "sfx_timer.h"
+
+
+#define FREQ 60
+#define DELAY (1000000 / FREQ)
+
+typedef void (*scummvm_timer_callback_t)(void *);
+static scummvm_timer_callback_t scummvm_timer_callback = NULL;
+static void *scummvm_timer_callback_data = NULL;
+extern ::Engine *g_engine;
+
+void scummvm_timer_update_internal(void *ptr) {
+ if (scummvm_timer_callback)
+ scummvm_timer_callback(scummvm_timer_callback_data);
+}
+
+int scummvm_timer_start(void (*func)(void *), void *data) {
+ if (scummvm_timer_callback) {
+ fprintf(stderr,
+ "Error: Attempt to initialize gametick timer more than once\n");
+ return SFX_ERROR;
+ }
+
+ if (!func) {
+ fprintf(stderr,
+ "Error: Attempt to initialize gametick timer w/o callback\n");
+ return SFX_ERROR;
+ }
+
+ scummvm_timer_callback = func;
+ scummvm_timer_callback_data = data;
+
+ ::g_engine->_timer->installTimerProc(&scummvm_timer_update_internal, DELAY, NULL);
+ return SFX_OK;
+}
+
+int scummvm_timer_stop() {
+ scummvm_timer_callback = NULL;
+ return SFX_OK;
+}
+
+
+sfx_timer_t sfx_timer_scummvm = {
+ "ScummVM",
+ "0.1",
+ DELAY/1000, 0,
+ NULL,
+ &scummvm_timer_start,
+ &scummvm_timer_stop
+ };
diff --git a/engines/sci/sfx/timer/sigalrm.c b/engines/sci/sfx/timer/sigalrm.c
new file mode 100644
index 0000000000..40cc2872e1
--- /dev/null
+++ b/engines/sci/sfx/timer/sigalrm.c
@@ -0,0 +1,157 @@
+/***************************************************************************
+ sigalrm.c Copyright (C) 2002 Christoph Reichenbach
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+
+#include <sfx_timer.h>
+
+#ifdef HAVE_SETITIMER
+
+#include <sys/time.h>
+#include <signal.h>
+#include <stdio.h>
+
+#ifdef HAVE_PTHREAD
+#include <pthread.h>
+#define sigprocmask pthread_sigmask
+#endif
+
+static void (*sig_callback)(void *) = NULL;
+static void *sig_callback_data = NULL;
+static sigset_t current_sigset;
+
+static void
+timer_handler(int i)
+{
+ if (sig_callback)
+ sig_callback(sig_callback_data);
+}
+
+static int
+sigalrm_set_option(char *name, char *value)
+{
+ return SFX_ERROR;
+}
+
+
+static int
+sigalrm_start(void)
+{
+ struct itimerval itimer;
+
+ itimer.it_value.tv_sec = 0;
+ itimer.it_value.tv_usec = 1000000/60;
+ itimer.it_interval = itimer.it_value;
+
+ signal(SIGALRM, timer_handler); /* Re-instate timer handler, to make sure */
+ setitimer(ITIMER_REAL, &itimer, NULL);
+
+ return SFX_OK;
+}
+
+
+static int
+sigalrm_init(void (*callback)(void *), void *data)
+{
+ if (sig_callback) {
+ fprintf(stderr, "Error: Attempt to initialize sigalrm timer more than once\n");
+ return SFX_ERROR;
+ }
+
+ if (!callback) {
+ fprintf(stderr, "Error: Attempt to initialize sigalrm timer w/o callback\n");
+ return SFX_ERROR;
+ }
+
+ sig_callback = callback;
+ sig_callback_data = data;
+
+ sigalrm_start();
+
+ sigemptyset(&current_sigset);
+ sigaddset(&current_sigset, SIGALRM);
+
+ return SFX_OK;
+}
+
+
+static int
+sigalrm_stop(void)
+{
+ struct itimerval itimer;
+
+ if (!sig_callback) {
+ fprintf(stderr, "Error: Attempt to stop sigalrm timer when not running\n");
+ return SFX_ERROR;
+ }
+
+ itimer.it_value.tv_sec = 0;
+ itimer.it_value.tv_usec = 0;
+ itimer.it_interval = itimer.it_value;
+
+ setitimer(ITIMER_REAL, &itimer, NULL); /* Stop timer */
+ signal(SIGALRM, SIG_DFL);
+
+ return SFX_OK;
+}
+
+
+static int
+sigalrm_block(void)
+{
+ if (sigprocmask(SIG_BLOCK, &current_sigset, NULL) != 0) {
+ fprintf(stderr, "Error: Failed to block sigalrm\n");
+ return SFX_ERROR;
+ }
+
+ return SFX_OK;
+}
+
+
+static int
+sigalrm_unblock(void)
+{
+ if (sigprocmask(SIG_UNBLOCK, &current_sigset, NULL) != 0) {
+ fprintf(stderr, "Error: Failed to unblock sigalrm\n");
+ return SFX_ERROR;
+ }
+
+ return SFX_OK;
+}
+
+
+sfx_timer_t sfx_timer_sigalrm = {
+ "sigalrm",
+ "0.1",
+ 17, /* 1000 / 60 */
+ 0,
+ &sigalrm_set_option,
+ &sigalrm_init,
+ &sigalrm_stop,
+ &sigalrm_block,
+ &sigalrm_unblock
+};
+
+#endif /* HAVE_SETITIMER */
diff --git a/engines/sci/sfx/timer/timers.c b/engines/sci/sfx/timer/timers.c
new file mode 100644
index 0000000000..b2a51d6062
--- /dev/null
+++ b/engines/sci/sfx/timer/timers.c
@@ -0,0 +1,73 @@
+/***************************************************************************
+ timers.c Copyright (C) 2002 Christoph Reichenbach
+
+
+ This program may be modified and copied freely according to the terms of
+ the GNU general public license (GPL), as long as the above copyright
+ notice and the licensing information contained herein are preserved.
+
+ Please refer to www.gnu.org for licensing details.
+
+ This work is provided AS IS, without warranty of any kind, expressed or
+ implied, including but not limited to the warranties of merchantibility,
+ noninfringement, and fitness for a specific purpose. The author will not
+ be held liable for any damage caused by this work or derivatives of it.
+
+ By using this source code, you agree to the licensing terms as stated
+ above.
+
+
+ Please contact the maintainer for bug reports or inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+
+#include <sfx_timer.h>
+#include <resource.h>
+
+#ifdef SCUMMVM
+extern sfx_timer_t sfx_timer_scummvm;
+#else // SCUMMVM
+
+#ifdef HAVE_SETITIMER
+extern sfx_timer_t sfx_timer_sigalrm;
+#endif
+
+#ifdef _DREAMCAST
+extern sfx_timer_t sfx_timer_pthread;
+#endif
+#endif // SCUMMVM
+
+sfx_timer_t *sfx_timers[] = {
+#ifdef SCUMMVM
+ &sfx_timer_scummvm,
+#else // SCUMMVM
+#ifdef HAVE_SETITIMER
+ &sfx_timer_sigalrm,
+#endif
+#ifdef _DREAMCAST
+ &sfx_timer_pthread,
+#endif
+#endif // SCUMMVM
+ NULL
+};
+
+
+sfx_timer_t *
+sfx_find_timer(char *name)
+{
+ if (!name) {
+ /* Policies go here */
+ return sfx_timers[0];
+ } else {
+ int n = 0;
+ while (sfx_timers[n]
+ && strcasecmp(sfx_timers[n]->name, name))
+ ++n;
+
+ return sfx_timers[n];
+ }
+}
diff --git a/engines/sci/sfx/timetest.c b/engines/sci/sfx/timetest.c
new file mode 100644
index 0000000000..80d9ad1b78
--- /dev/null
+++ b/engines/sci/sfx/timetest.c
@@ -0,0 +1,59 @@
+/***************************************************************************
+ timetest.c Copyright (C) 2003 Christoph Reichenbach
+
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public Licence as
+ published by the Free Software Foundaton; either version 2 of the
+ Licence, or (at your option) any later version.
+
+ It is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ merchantibility or fitness for a particular purpose. See the
+ GNU General Public Licence for more details.
+
+ You should have received a copy of the GNU General Public Licence
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+
+ Please contact the maintainer for any program-related bug reports or
+ inquiries.
+
+ Current Maintainer:
+
+ Christoph Reichenbach (CR) <jameson@linuxgames.com>
+
+***************************************************************************/
+
+#include <stdio.h>
+#include <sfx_time.h>
+#include <assert.h>
+
+sfx_timestamp_t a,b,c;
+
+int
+main(int argc, char **argv)
+{
+ int i;
+ a = sfx_new_timestamp(10, 0, 1000);
+ b = sfx_new_timestamp(10, 1000, 1000);
+ c = sfx_new_timestamp(10, 2000, 1000);
+
+ assert(sfx_timestamp_sample_diff(a, b) == -1);
+ assert(sfx_timestamp_sample_diff(b, a) == 1);
+ assert(sfx_timestamp_sample_diff(c, a) == 2);
+ assert(sfx_timestamp_sample_diff(sfx_timestamp_add(b, 2000), a) == 2001);
+ assert(sfx_timestamp_sample_diff(sfx_timestamp_add(b, 2000), sfx_timestamp_add(a, -1000)) == 3001);
+
+ for (i = -10000; i < 10000; i++) {
+ int v = sfx_timestamp_sample_diff(sfx_timestamp_add(c, i), c);
+ if (v != i) {
+ fprintf(stderr, "After adding %d samples: Got diff of %d\n", i, v);
+ return 1;
+ }
+ }
+
+ return 0;
+}