diff options
| author | Jordi Vilalta Prat | 2009-02-15 06:10:59 +0000 | 
|---|---|---|
| committer | Jordi Vilalta Prat | 2009-02-15 06:10:59 +0000 | 
| commit | fa6e10e9cec163845aa29e7940c86e9c9ab8a2bc (patch) | |
| tree | ce87338830cc8c149e1de545246bcefe4f45da00 /engines/sci/sfx/core.c | |
| parent | 7c148ddf021c990fa866b7600f979aac9a5b26c9 (diff) | |
| download | scummvm-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.c | 938 | 
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 = ¬_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 == ¬_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); +} | 
