aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/sfx/core.c
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/core.c
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/core.c')
-rw-r--r--engines/sci/sfx/core.c938
1 files changed, 938 insertions, 0 deletions
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);
+}