Hi,
I'll attempt explain the Win32 method. If someone can please detail how the
UNIX/cross-platform method would/could work or fit in with this, that would be
wonderful.
First, sending messages. There are two ways you can do it. Either use a variant
of PostMessage which puts the message in a queue associated with the current
thread or another thread, or use a variant of SendMessage which sends the
message immediately.
The second way sounds better since it's 'immediate', but there are more risks
with deadlocks. I'll describe the SendMessage variants. The SendMessage call
itself waits for the message handler to process the message before it returns.
If the message handler relies on something from the thread sending the message -
deadlock. SendNotifyMessage returns immediately if the message is being sent
to a different thread, or waits for the message to be processed if the sender
and receiver is the current thread. If SendMessageCallback is used, the call
returns immediately, and then a callback function is called once message
processing has finished. This extra callback functionality could be useful in
some cases but probably not in general. At least it always returns immediately
though. You can also use SendMessageTimeout which waits a given amount of time
for the message to be handled before giving up and returning - but only if the
message is being sent to a different thread. So the best option here assuming
sound events are being sent and received by the same thread (which could be
wrong - it depends on how the cross-platform approach is implemented), is by
using SendMessageCallback.
Using PostMessage on the other hand is a piece of cake - it just might be a
little slower. I've run tests on this and about 5 messages get sent to the
sound thread when the app is starting up, and a few others while running when
windows get minimized, etc...
So that's sending and posting messages. Receiving them is a little 'hand-
wavey'. In a Win32 app, messages are passed to windows. They don't have to be
visible or anything, a window class just has to be defined in each thread to
handle the messages. This is done in some sort of init function as follows:
/* register window class */
memset (&wndclass, 0, sizeof(WNDCLASS));
wndclass.lpfnWndProc = MainWndProc;
wndclass.hInstance = GetModuleHandle(NULL);
wndclass.lpszClassName = g_szMainWndClass;
if (!RegisterClass(&wndclass))
{
fprintf(stderr, "can't register window class\n");
}
/* create our 'window' */
hWndSoundThread = CreateWindow (
g_szMainWndClass,
"FreeSCI Sound",
WS_DISABLED,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
NULL,
NULL,
NULL,
NULL
);
The name of the message handling/callback function can be anything, but has to
be defined as:
LRESULT CALLBACK
MainWndProc (HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
The inside of the function is a piece of cake:
DECLARE_MSG_IDS() /* declare unique Windows message IDs */
if (nMsg == UWM_SOUND_COMMAND)
{
/* do whatever to handle it here */
return 0;
}
else if (nMsg == UWM_SOUND_SEND_DATA)
{ ... }
...
else
{
/* pass on to default Windows message handler */
return DefWindowProc (hwnd, nMsg, wParam, lParam);
}
And that's about it. My question is, can the UNIX/cross-platform event-driven
sound server be based around this or work in this way?
I'll attach my kludge effort of putting an event-driven Win32 sound server
within a polled one to demonstrate. I did this a couple of weeks ago, and I
suspect that my explanation above is a lot clearer than these files. They also
have a lot of testing stuff and crap in there, and I don't know if it compiles,
but hopefully you can see what I'm getting at! If anyone else wants me to e-
mail them a copy since attachments get filtered out in the list, then please e-
mail me directly.
Cheers,
Alex.
-- Attached file included as plaintext by Listar --
-- File: soundserver.c
/***************************************************************************
soundserver.c Copyright (C) 1999 Christoph Reichenbach, TU Darmstadt
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) [[EMAIL PROTECTED]]
***************************************************************************/
#include <sciresource.h>
#include <stdarg.h>
#include <engine.h>
#include <sound.h>
#include <scitypes.h>
#include <soundserver.h>
#include <midi_device.h>
#include <sys/types.h>
#ifdef _WINDOWS
#include <win32/win32_messages.h>
#endif
sound_server_t *global_sound_server = NULL;
int soundserver_dead = 0;
#define POLYPHONY(song, channel) song->data[(channel << 1) + 1]
static void
sound_server_print_channels_any(FILE *ds, int *channel_instrument,
byte *mute_channel, int flag)
{
int i;
for (i = 0; i < 16; i++)
if (channel_instrument[i] >= 0) {
fprintf(ds, "#%d%s:", i+1, mute_channel[i]? " [MUTE]":"");
if (flag)
midi_mt32gm_print_instrument(ds,
channel_instrument[i]);
else
fprintf(ds, " Instrument %d\n", channel_instrument[i]);
}
}
static void
sound_server_print_channels(FILE *ds, int *channel_instrument,
byte *mute_channel)
{
sound_server_print_channels_any(ds, channel_instrument, mute_channel, 0);
}
static void
sound_server_print_mapped_channels(FILE *ds, int *channel_instrument,
byte *mute_channel)
{
sound_server_print_channels_any(ds, channel_instrument, mute_channel, 1);
}
#define SOUNDSERVER_INSTRMAP_CHANGE(ELEMENT, VAL_MIN, VAL_MAX, ELEMENT_NAME) \
if (value >= (VAL_MIN) && value <= (VAL_MAX)) \
MIDI_mapping[instr].ELEMENT = value; \
else \
fprintf(output, "Invalid %s: %d\n", ELEMENT_NAME, value) \
void
sound_server_change_instrmap(FILE *output, int action, int instr, int value)
{
if (instr < 0 || instr >= MIDI_mappings_nr) {
fprintf(output, "sound_server_change_instrmap(): Attempt to re-map"
" invalid instrument %d!\n", instr);
return;
}
switch (action) {
case SOUND_COMMAND_INSTRMAP_SET_INSTRUMENT:
if ((value >= (0) && value <= (MIDI_mappings_nr - 1))
|| value == NOMAP
|| value == RHYTHM)
MIDI_mapping[instr].gm_instr = value;
else
fprintf(output, "Invalid instrument ID: %d\n", value);
break;
case SOUND_COMMAND_INSTRMAP_SET_KEYSHIFT:
SOUNDSERVER_INSTRMAP_CHANGE(keyshift, -128, 127,
"key shift");
break;
case SOUND_COMMAND_INSTRMAP_SET_FINETUNE:
SOUNDSERVER_INSTRMAP_CHANGE(finetune, -32768, 32767,
"finetune value");
break;
case SOUND_COMMAND_INSTRMAP_SET_BENDER_RANGE:
SOUNDSERVER_INSTRMAP_CHANGE(bender_range, -128, 127,
"bender range");
break;
case SOUND_COMMAND_INSTRMAP_SET_PERCUSSION:
SOUNDSERVER_INSTRMAP_CHANGE(gm_rhythmkey, 0, 79,
"percussion instrument");
break;
case SOUND_COMMAND_INSTRMAP_SET_VOLUME:
SOUNDSERVER_INSTRMAP_CHANGE(volume, 0, 100,
"instrument volume");
break;
default:
fprintf(output, "sound_server_change_instrmap(): Internal error: "
"Invalid action %d!\n", action);
}
}
void
sci0_soundserver(int reverse_stereo)
{
GTimeVal last_played, wakeup_time, ctime;
byte mute_channel[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0};
playing_notes_t playing_notes[16];
int channel_instrument_orig[16] = {-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1};
int channel_instrument[16];
song_t *_songp = NULL;
songlib_t songlib = &_songp; /* Song library */
song_t *newsong, *song = NULL; /* The song we're playing */
int master_volume = 0; /* the master volume.. whee */
int debugging = 0; /* Debugging enabled? */
int command = 0;
int ccc = 127; /* cumulative cue counter */
int suspended = 0; /* Used to suspend the sound server */
int i;
GTimeVal suspend_time; /* Time at which the sound server was suspended */
DECLARE_MSG_IDS()
for (i = 0; i < 16; i++)
playing_notes[i].playing = 0;
memset(&suspend_time, 0, sizeof(GTimeVal));
memset(&wakeup_time, 0, sizeof(GTimeVal));
memset(&ctime, 0, sizeof(GTimeVal));
sci_get_current_time(&last_played);
fprintf(ds, "Sound server initialized\n");
while (!soundserver_dead)
{
sound_event_t *event_temp;
GTimeVal wait_tv;
int ticks = 0; /* Ticks to next command */
int old_songpos = 33; /* initial positiion */
song_t *oldsong = song;
byte last_command = 0; /* Used for 'running' mode */
fflush(ds);
if (song && (song->fading == 0)) { /* Finished fading? */
printf("song %04x faded out \n", song->handle);
song->status = SOUND_STATUS_STOPPED;
midi_allstop();
song->pos = 33;
song->loopmark = 33; /* Reset position */
#ifdef _WINDOWS
if (master_thread_id != sound_thread_id)
{
//printf("GOOD: master thread != sound thread so using SendNotifyMessage\n");
SendNotifyMessage(hWndSoundThread, UWM_SOUND_SIGNAL_LOOP,
(WPARAM)song->handle, -1);
SendNotifyMessage(hWndSoundThread, UWM_SOUND_SIGNAL_FINISHED,
(WPARAM)song->handle, 0);
}
else
{
//printf("NOTS: master thread == sound thread so using SendMessage\n");
SendMessage(NULL, UWM_SOUND_SIGNAL_LOOP, (WPARAM)song->handle,
-1);
SendMessage(NULL, UWM_SOUND_SIGNAL_FINISHED,
(WPARAM)song->handle, 0);
}
#else
sound_queue_event(song->handle, SOUND_SIGNAL_LOOP, -1);
sound_queue_event(song->handle, SOUND_SIGNAL_FINISHED, 0);
#endif
}
song = song_lib_find_active(songlib, song);
if (song == NULL)
{
sci_get_current_time((GTimeVal *)&last_played);
ticks = 60; /* Wait a second for new commands, then collect your new ticks here.
*/
}
if (ticks == 0)
{
int tempticks;
sci_get_current_time((GTimeVal *)&last_played);
ticks = 0;
old_songpos = song->pos;
/* Handle length escape sequence */
while ((tempticks = song->data[(song->pos)++]) == SCI_MIDI_TIME_EXPANSION_PREFIX)
ticks += SCI_MIDI_TIME_EXPANSION_LENGTH;
ticks += tempticks;
}
/*--------------*/
/* Handle input */
/*--------------*/
newsong = song;
if (ticks) do {
GTimeVal *wait_tvp;
if (!suspended) {
wakeup_time = song_next_wakeup_time(&last_played, ticks);
wait_tv = song_sleep_time(&last_played, ticks);
wait_tvp = &wait_tv;
} else {
/* Sound server is suspended */
wait_tvp = NULL; /* select()s infinitely long */
}
event_temp = sound_get_command(wait_tvp);
/* Wait for input: */
if (event_temp) { /* We've got mail! */
sound_event_t event = *event_temp;
song_t *modsong;
free(event_temp);
modsong = song_lib_find(songlib, event.handle);
switch (event.signal) {
case SOUND_COMMAND_INIT_SONG: {
byte *data;
int totalsize = 0;
if (debugging)
fprintf(ds, "Receiving song for handle %04x: ", event.handle);
if (modsong) {
int lastmode = song_lib_remove(songlib, event.handle);
if (lastmode == SOUND_STATUS_PLAYING) {
newsong = songlib[0];
/* Force song detection to start with the highest priority song */
#ifdef _WINDOWS
if (master_thread_id != sound_thread_id)
{
//printf("GOOD: master thread != sound thread so using SendNotifyMessage\n");
SendNotifyMessage(hWndSoundThread, UWM_SOUND_SIGNAL_FINISHED,
(WPARAM)event.handle, 0);
}
else
{
//printf("NOTS: master thread == sound thread so using SendMessage\n");
SendMessage(NULL, UWM_SOUND_SIGNAL_FINISHED,
(WPARAM)event.handle, 0);
}
#else
sound_queue_event(event.handle, SOUND_SIGNAL_FINISHED, 0);
#endif
}
if (modsong == song)
song = NULL;
}
sound_get_data(&data,&totalsize,sizeof(int));
if (debugging)
fprintf(ds, "OK\n");
modsong = song_new(event.handle, data, totalsize, event.value);
if (midi_playrhythm)
modsong->flags[RHYTHM_CHANNEL] |= midi_playflag;
song_lib_add(songlib, modsong);
ccc = 127; /* Reset ccc */
/* set default reverb */
/* midi_reverb(-1); */
/* midi_allstop(); */
#ifdef _WINDOWS
if (master_thread_id != sound_thread_id)
{
//printf("GOOD: master thread != sound thread so using SendNotifyMessage\n");
SendNotifyMessage(hWndSoundThread,
UWM_SOUND_SIGNAL_INITIALIZED, (WPARAM)event.handle, 0);
}
else
{
//printf("NOTS: master thread == sound thread so using SendMessage\n");
SendMessage(NULL, UWM_SOUND_SIGNAL_INITIALIZED,
(WPARAM)event.handle, 0);
}
#else
sound_queue_event(event.handle, SOUND_SIGNAL_INITIALIZED, 0);
#endif
}
break;
case SOUND_COMMAND_PLAY_HANDLE:
if (debugging)
fprintf(ds, "Playing handle %04x\n", event.handle);
if (modsong) {
midi_allstop();
modsong->status = SOUND_STATUS_PLAYING;
#ifdef _WINDOWS
if (master_thread_id != sound_thread_id)
{
//printf("GOOD: master thread != sound thread so using SendNotifyMessage\n");
SendNotifyMessage(hWndSoundThread, UWM_SOUND_SIGNAL_PLAYING,
(WPARAM)event.handle, 0);
}
else
{
//printf("NOTS: master thread == sound thread so using SendMessage\n");
SendMessage(NULL, UWM_SOUND_SIGNAL_PLAYING,
(WPARAM)event.handle, 0);
}
#else
sound_queue_event(event.handle, SOUND_SIGNAL_PLAYING, 0);
#endif
newsong = modsong; /* Play this song */
memcpy(channel_instrument, channel_instrument_orig, sizeof(int)*16);
} else
fprintf(ds, "Attempt to play invalid handle %04x\n", event.handle);
break;
case SOUND_COMMAND_SET_LOOPS:
if (debugging)
fprintf(ds, "Set loops to %d on handle %04x\n", event.value, event.handle);
if (modsong)
modsong->loops = event.value;
else
fprintf(ds, "Attempt to set loops on invalid handle %04x\n", event.handle);
break;
case SOUND_COMMAND_DISPOSE_HANDLE:
if (debugging)
fprintf(ds, "Disposing handle %04x (value %04x)\n", event.handle,
event.value);
if (modsong) {
int lastmode = song_lib_remove(songlib, event.handle);
if (lastmode == SOUND_STATUS_PLAYING) {
newsong = songlib[0]; /* Force song detection to start with the highest
priority song */
#ifdef _WINDOWS
if (master_thread_id != sound_thread_id)
{
//printf("GOOD: master thread != sound thread so using SendNotifyMessage\n");
SendNotifyMessage(hWndSoundThread, UWM_SOUND_SIGNAL_FINISHED,
(WPARAM)event.handle, 0);
}
else
{
//printf("NOTS: master thread == sound thread so using SendMessage\n");
SendMessage(NULL, UWM_SOUND_SIGNAL_FINISHED,
(WPARAM)event.handle, 0);
}
#else
sound_queue_event(event.handle, SOUND_SIGNAL_FINISHED, 0);
#endif
}
} else
fprintf(ds, "Attempt to dispose invalid handle %04x\n", event.handle);
if (modsong == song)
song = NULL;
break;
case SOUND_COMMAND_STOP_HANDLE:
if (debugging)
fprintf(ds, "Stopping handle %04x (value %04x)\n", event.handle,
event.value);
if (modsong) {
midi_allstop();
modsong->status = SOUND_STATUS_STOPPED;
if (modsong->resetflag) { /* only reset if we are supposed to. */
modsong->pos = 33;
modsong->loopmark = 33; /* Reset position */
}
#ifdef _WINDOWS
if (master_thread_id != sound_thread_id)
{
//printf("GOOD: master thread != sound thread so using SendNotifyMessage\n");
SendNotifyMessage(hWndSoundThread, UWM_SOUND_SIGNAL_FINISHED,
(WPARAM)event.handle, 0);
}
else
{
//printf("NOTS: master thread == sound thread so using SendMessage\n");
SendMessage(NULL, UWM_SOUND_SIGNAL_FINISHED,
(WPARAM)event.handle, 0);
}
#else
sound_queue_event(event.handle, SOUND_SIGNAL_FINISHED, 0);
#endif
} else
fprintf(ds, "Attempt to stop invalid handle %04x\n", event.handle);
break;
case SOUND_COMMAND_SUSPEND_HANDLE: {
song_t *seeker = *songlib;
if (event.handle) {
while (seeker && (seeker->status != SOUND_STATUS_SUSPENDED))
seeker = seeker->next;
if (seeker) {
seeker->status = SOUND_STATUS_WAITING;
if (debugging)
fprintf(ds, "Un-suspending paused song\n");
}
} else {
while (seeker && (seeker->status != SOUND_STATUS_WAITING)) {
if (seeker->status == SOUND_STATUS_SUSPENDED)
return;
seeker = seeker->next;
}
if (seeker) {
seeker->status = SOUND_STATUS_SUSPENDED;
fprintf(ds, "Suspending paused song\n");
}
}
/*
if (debugging)
fprintf(ds, "Suspending handle %04x (value %04x)\n", event.handle,
event.value);
if (modsong) {
modsong->status = SOUND_STATUS_SUSPENDED;
sound_queue_event(event.handle, SOUND_SIGNAL_PAUSED, 0);
} else
fprintf(ds, "Attempt to suspend invalid handle %04x\n", event.handle);
*/ /* Old code. */
}
break;
case SOUND_COMMAND_RESUME_HANDLE:
if (debugging)
fprintf(ds, "Resuming on handle %04x (value %04x)\n", event.handle,
event.value);
if (modsong) {
if (modsong->status == SOUND_STATUS_SUSPENDED) {
modsong->status = SOUND_STATUS_WAITING;
#ifdef _WINDOWS
if (master_thread_id != sound_thread_id)
{
//printf("GOOD: master thread != sound thread so using SendNotifyMessage\n");
SendNotifyMessage(hWndSoundThread, UWM_SOUND_SIGNAL_RESUMED,
(WPARAM)event.handle, 0);
}
else
{
//printf("NOTS: master thread == sound thread so using SendMessage\n");
SendMessage(NULL, UWM_SOUND_SIGNAL_RESUMED,
(WPARAM)event.handle, 0);
}
#else
sound_queue_event(event.handle, SOUND_SIGNAL_RESUMED, 0);
#endif
} else
fprintf(ds, "Attempt to resume handle %04x although not suspended\n",
event.handle);
} else
fprintf(ds, "Attempt to resume invalid handle %04x\n", event.handle);
break;
case SOUND_COMMAND_SET_VOLUME:
if (debugging)
fprintf(ds, "Set volume to %d\n", event.value);
master_volume = event.value * 100 / 15; /* scale to % */
midi_volume(master_volume);
break;
case SOUND_COMMAND_SET_MUTE:
fprintf(ds, "Called mute??? should never happen\n");
break;
case SOUND_COMMAND_RENICE_HANDLE:
if (debugging)
fprintf(ds, "Renice to %d on handle %04x\n", event.value, event.handle);
if (modsong) {
modsong->priority = event.value;
song_lib_resort(songlib, modsong); /* Re-sort it according to the new
priority */
} else
fprintf(ds, "Attempt to renice on invalid handle %04x\n", event.handle);
break;
case SOUND_COMMAND_FADE_HANDLE:
if (debugging)
fprintf(ds, "Fading %d on handle %04x\n", event.value, event.handle);
if (event.handle == 0x0000) {
if (song) {
song->fading = event.value;
song->maxfade = event.value;
}
} else if (modsong) {
modsong->fading = event.value;
} else {
fprintf(ds, "Attempt to fade on invalid handle %04x\n", event.handle);
}
break;
case SOUND_COMMAND_TEST: {
int i = midi_polyphony;
if (debugging)
fprintf(ds, "Received TEST signal. Responding...\n");
sound_send_data((byte *) &i, sizeof(int));
} break;
case SOUND_COMMAND_SAVE_STATE: {
char *dirname;
int success;
long usecs;
int size;
GTimeVal currtime;
sound_get_data((byte **)&dirname,&size,event.value);
sci_get_current_time(&currtime);
usecs = (currtime.tv_sec - last_played.tv_sec) * 1000000
+ (currtime.tv_usec - last_played.tv_usec);
success = soundsrv_save_state(ds,
global_sound_server->flags &
SOUNDSERVER_FLAG_SEPARATE_CWD? dirname : NULL,
songlib, newsong,
ccc, usecs, ticks, 0, master_volume);
/* Return soundsrv_save_state()'s return value */
sound_send_data((byte *)&success, sizeof(int));
free(dirname);
}
break;
case SOUND_COMMAND_RESTORE_STATE: {
char *dirname;
int success;
long usecs, secs;
int len;
int fadeticks_obsolete;
sound_get_data((byte **)&dirname, &len, event.value);
sci_get_current_time(&last_played);
success = soundsrv_restore_state(ds,
global_sound_server->flags &
SOUNDSERVER_FLAG_SEPARATE_CWD? dirname : NULL,
songlib, &newsong,
&ccc, &usecs, &ticks, &fadeticks_obsolete,
&master_volume);
last_played.tv_sec -= secs = (usecs - last_played.tv_usec) / 1000000;
last_played.tv_usec -= (usecs + secs * 1000000);
/* Return return value */
sound_send_data((byte *)&success, sizeof(int));
/* REPORT_STATUS(success); */
song = newsong;
free(dirname);
/* restore some device state */
if (newsong) {
int i;
midi_allstop();
midi_volume(master_volume);
for (i = 0; i < MIDI_CHANNELS; i++) {
if (newsong->instruments[i])
midi_event2(0xc0 | i, newsong->instruments[i]);
}
midi_reverb(newsong->reverb);
}
}
break;
case SOUND_COMMAND_PRINT_SONGID: {
if (song)
fprintf(ds, "%04x", song->handle);
else
fprintf(ds, "No song is playing.");
}
break;
case SOUND_COMMAND_PRINT_CHANNELS:
sound_server_print_channels(ds, &(channel_instrument[0]),
&(mute_channel[0]));
break;
case SOUND_COMMAND_PRINT_MAPPING:
sound_server_print_mapped_channels(ds, &(channel_instrument[0]),
&(mute_channel[0]));
break;
case SOUND_COMMAND_INSTRMAP_SET_INSTRUMENT:
case SOUND_COMMAND_INSTRMAP_SET_KEYSHIFT:
case SOUND_COMMAND_INSTRMAP_SET_FINETUNE:
case SOUND_COMMAND_INSTRMAP_SET_BENDER_RANGE:
case SOUND_COMMAND_INSTRMAP_SET_PERCUSSION:
case SOUND_COMMAND_INSTRMAP_SET_VOLUME:
sound_server_change_instrmap(ds, event.signal,
event.handle, event.value);
break;
case SOUND_COMMAND_MUTE_CHANNEL:
if (event.value >= 0 && event.value < 16)
mute_channel[event.value] = 1;
break;
case SOUND_COMMAND_UNMUTE_CHANNEL:
if (event.value >= 0 && event.value < 16)
mute_channel[event.value] = 0;
break;
case SOUND_COMMAND_SUSPEND_SOUND: {
sci_get_current_time((GTimeVal *)&suspend_time);
suspended = 1;
}
break;
case SOUND_COMMAND_RESUME_SOUND: {
GTimeVal resume_time;
sci_get_current_time((GTimeVal *)&resume_time);
/* Modify last_played relative to wakeup_time - suspend_time */
last_played.tv_sec += resume_time.tv_sec - suspend_time.tv_sec - 1;
last_played.tv_usec += resume_time.tv_usec - suspend_time.tv_usec + 1000000;
/* Make sure that 0 <= tv_usec <= 999999 */
last_played.tv_sec -= last_played.tv_usec / 1000000;
last_played.tv_usec %= 1000000;
suspended = 0;
}
break;
case SOUND_COMMAND_STOP_ALL: {
song_t *seeker = *songlib;
while (seeker) {
if ((seeker->status == SOUND_STATUS_WAITING)
|| (seeker->status == SOUND_STATUS_PLAYING)) {
seeker->status = SOUND_STATUS_STOPPED;
#ifdef _WINDOWS
if (master_thread_id != sound_thread_id)
{
//printf("GOOD: master thread != sound thread so using SendNotifyMessage\n");
SendNotifyMessage(hWndSoundThread, UWM_SOUND_SIGNAL_FINISHED,
(WPARAM)event.handle, 0);
}
else
{
//printf("NOTS: master thread == sound thread so using SendMessage\n");
SendMessage(NULL, UWM_SOUND_SIGNAL_FINISHED,
(WPARAM)event.handle, 0);
}
#else
sound_queue_event(seeker->handle, SOUND_SIGNAL_FINISHED, 0);
#endif
}
seeker = seeker->next;
}
newsong = NULL;
}
midi_allstop();
break;
case SOUND_COMMAND_SHUTDOWN:
fprintf(stderr,"Sound server: Received shutdown signal\n");
midi_close();
song_lib_free(songlib);
soundserver_dead = 1;
return;
default:
fprintf(ds, "Received invalid signal: %d\n", event.signal);
}
}
sci_get_current_time((GTimeVal *)&ctime);
} while (suspended
|| (wakeup_time.tv_sec > ctime.tv_sec)
|| ((wakeup_time.tv_sec == ctime.tv_sec)
&& (wakeup_time.tv_usec > ctime.tv_usec)));
/*---------------*/
/* Handle output */
/*---------------*/
if (newsong != oldsong && newsong) {
int i;
for (i = 0; i < 16; i++) {
playing_notes[i].polyphony = MAX(1, MIN(POLYPHONY(newsong, i),
MAX_POLYPHONY));
playing_notes[i].playing = 0;
/* Lingering notes are usually intended */
}
}
if (song && song->data) {
int newcmd;
int param, param2 = -1;
newcmd = song->data[song->pos];
if (newcmd & 0x80) {
++(song->pos);
command = newcmd;
} /* else we've got the 'running status' mode defined in the MIDI standard */
if (command == SCI_MIDI_EOT) {
if ((--(song->loops) != 0) && song->loopmark) {
if (debugging)
fprintf(ds, "looping back from %d to %d on handle %04x\n",
song->pos, song->loopmark, song->handle);
song->pos = song->loopmark;
#ifdef _WINDOWS
if (master_thread_id != sound_thread_id)
{
//printf("GOOD: master thread != sound thread so using SendNotifyMessage\n");
SendNotifyMessage(hWndSoundThread, UWM_SOUND_SIGNAL_LOOP,
(WPARAM)song->handle, song->loops);
}
else
{
//printf("NOTS: master thread == sound thread so using SendMessage\n");
SendMessage(NULL, UWM_SOUND_SIGNAL_LOOP, (WPARAM)song->handle,
song->loops);
}
#else
sound_queue_event(song->handle, SOUND_SIGNAL_LOOP, song->loops);
#endif
} else { /* Finished */
song->status = SOUND_STATUS_STOPPED;
song->pos = 33;
song->loopmark = 33; /* Reset position */
#ifdef _WINDOWS
if (master_thread_id != sound_thread_id)
{
//printf("GOOD: master thread != sound thread so using SendNotifyMessage\n");
SendNotifyMessage(hWndSoundThread, UWM_SOUND_SIGNAL_LOOP,
(WPARAM)song->handle, -1);
SendNotifyMessage(hWndSoundThread, UWM_SOUND_SIGNAL_FINISHED,
(WPARAM)song->handle, 0);
}
else
{
//printf("NOTS: master thread == sound thread so using SendMessage\n");
SendMessage(NULL, UWM_SOUND_SIGNAL_LOOP, (WPARAM)song->handle,
-1);
SendMessage(NULL, UWM_SOUND_SIGNAL_FINISHED,
(WPARAM)song->handle, 0);
}
#else
sound_queue_event(song->handle, SOUND_SIGNAL_LOOP, -1);
sound_queue_event(song->handle, SOUND_SIGNAL_FINISHED, 0);
#endif
ticks = 1; /* Wait one tick, then continue with next song */
midi_allstop();
}
} else { /* Song running normally */
param = song->data[song->pos];
if (cmdlen[command >> 4] == 2)
param2 = song->data[song->pos + 1];
else
param2 = -1;
song->pos += cmdlen[command >> 4];
if (reverse_stereo
&& ((command & MIDI_CONTROL_CHANGE) == MIDI_CONTROL_CHANGE)
&& (param == MIDI_CC_PAN))
param2 = 0x7f - param2; /* Reverse stereo */
if ((command & 0xf0) == 0xc0) /* Change instrument */
channel_instrument[command & 0xf] = param;
if (!((command & 0xf0) == 0x90 && mute_channel[command & 0xf]))
/* Unless the channel is muted */
sci_midi_command(song, command, param, param2,
&ccc, ds, &(playing_notes[command & 0xf]));
}
if (song->fading >= 0) {
int fadeticks = ticks;
ticks = !!ticks;
song->fading -= fadeticks;
if (song->fading < 0)
song->fading = 0; /* Faded out after the ticks have run out */
}
}
if (oldsong != newsong) {
if (newsong) {
int i;
for (i = 0; i < MIDI_CHANNELS; i++) {
if (newsong->instruments[i])
midi_event2(0xc0 | i, newsong->instruments[i]);
}
}
if (song)
song->pos = old_songpos;
}
song = newsong;
}
/* implicitly quit */
}
void
sound_queue_event(int handle, int signal, int value) {
global_sound_server->queue_event(handle, signal, value);
}
void
sound_queue_command(int handle, int signal, int value) {
global_sound_server->queue_command(handle, signal, value);
}
sound_event_t *
sound_get_command(GTimeVal *wait_tvp) {
return global_sound_server->get_command(wait_tvp);
}
int sound_send_data(byte *data_ptr, int maxsend) {
return global_sound_server->send_data(data_ptr, maxsend);
}
int sound_get_data(byte **data_ptr, int *size, int maxlen){
return global_sound_server->get_data(data_ptr, size, maxlen);
}
-- Attached file included as plaintext by Listar --
-- File: soundserver_win32.c
/***************************************************************************
soundserver_win32.c Copyright (C) 2001 Alexander R Angas
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:
Alexander R Angas (Alex Angas) <[EMAIL PROTECTED]>
History:
20010901 - Adapted from soundserver_sdl.c by Solomon Peachy.
-- Alex Angas
***************************************************************************/
#define _WIN32_WINNT 0x0400 /* so can use SetThreadIdealProcessor */
#include <sci_memory.h>
#include <engine.h>
#include <soundserver.h>
#include <sound.h>
#ifdef WINVER
#include <windows.h>
#include <win32/win32_messages.h>
/* #define SSWIN_DEBUG */
static HANDLE child_thread;
static HANDLE out_mutex;
static HANDLE in_mutex;
static int reverse_stereo = 0;
extern sound_server_t sound_server_win32;
static const char g_szMainWndClass[] = "FreeSCISound";
static WNDCLASS wndclass;
/* Mutex names */
static LPCTSTR out_mutex_name = "SoundServerOutMutex";
static LPCTSTR in_mutex_name = "SoundServerInMutex";
static LPCTSTR bulk_mutex_name = "SoundServerBulkMutex";
static HANDLE bulk_mutices[2];
static sci_queue_t bulk_queues[2];
static const DWORD MUTEX_TIMEOUT = 5000L;
/* Used for manipulating name of bulk mutex */
static int mutex_name_len;
static char *mutex_name_temp;
static char *dump;
static sound_eq_t inqueue; /* The in-event queue */
static sound_eq_t ev_queue; /* The event queue */
/* function called when sound server child thread begins */
DWORD WINAPI
win32_soundserver_init(LPVOID lpP)
{
#ifdef SSWIN_DEBUG
fprintf(stderr, "SSWIN_DEBUG: TID%u - CHILD thread ID,
win32_soundserver_init()\n", GetCurrentThreadId());
#endif
/* store copy of sound thread id */
sound_thread_id = GetCurrentThreadId();
/* start the sound server */
sci0_soundserver(reverse_stereo);
return 0;
}
LRESULT CALLBACK
MainWndProc (HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
sound_event_t *event;
DECLARE_MSG_IDS()
if (nMsg == UWM_SOUND_COMMAND)
{
// printf("YAY! Got UWM_SOUND_COMMAND!!\n");
event = wParam;
sound_queue_command(event->handle, event->signal, event->value);
return 0;
}
else if (nMsg == UWM_SOUND_SEND_DATA)
{
// printf("YAY! Got UWM_SOUND_SEND_DATA!!\n");
if (sound_send_data(wParam, lParam) != lParam) {
fprintf(stderr, "sound_command(): sound_send_data"
" returned < count\n");
}
return 0;
}
else if (nMsg == UWM_SOUND_SIGNAL_ABSOLUTE_CUE)
{
// printf("YAY! Got UWM_SOUND_SIGNAL_ABSOLUTE_CUE!!\n");
sound_queue_event(wParam, SOUND_SIGNAL_ABSOLUTE_CUE, lParam);
return 0;
}
else if (nMsg == UWM_SOUND_SIGNAL_LOOP)
{
// printf("YAY! Got UWM_SOUND_SIGNAL_LOOP!!\n");
sound_queue_event(wParam, SOUND_SIGNAL_LOOP, lParam);
return 0;
}
else if (nMsg == UWM_SOUND_SIGNAL_FINISHED)
{
// printf("YAY! Got UWM_SOUND_SIGNAL_FINISHED!!\n");
sound_queue_event(wParam, SOUND_SIGNAL_FINISHED, lParam);
return 0;
}
else if (nMsg == UWM_SOUND_SIGNAL_INITIALIZED)
{
// printf("YAY! Got UWM_SOUND_SIGNAL_INITIALIZED!!\n");
sound_queue_event(wParam, SOUND_SIGNAL_INITIALIZED, lParam);
return 0;
}
else if (nMsg == UWM_SOUND_SIGNAL_PLAYING)
{
// printf("YAY! Got UWM_SOUND_SIGNAL_PLAYING!!\n");
sound_queue_event(wParam, SOUND_SIGNAL_PLAYING, lParam);
return 0;
}
else if (nMsg == UWM_SOUND_SIGNAL_FINISHED)
{
// printf("YAY! Got UWM_SOUND_SIGNAL_FINISHED!!\n");
sound_queue_event(wParam, SOUND_SIGNAL_FINISHED, lParam);
return 0;
}
else if (nMsg == UWM_SOUND_SIGNAL_RESUMED)
{
// printf("YAY! Got UWM_SOUND_SIGNAL_RESUMED!!\n");
sound_queue_event(wParam, SOUND_SIGNAL_RESUMED, lParam);
return 0;
}
else if (nMsg == UWM_SOUND_SIGNAL_LOOP)
{
// printf("YAY! Got UWM_SOUND_SIGNAL_LOOP!!\n");
sound_queue_event(wParam, SOUND_SIGNAL_LOOP, lParam);
return 0;
}
else if (nMsg == 28)
{} /* ignore - used if window receives focus */
else
{
fprintf(stderr, "soundserver_win32.c: MainWndProc got unrecognised
message %u\n", nMsg);
}
return DefWindowProc (hwnd, nMsg, wParam, lParam);
}
int
sound_win32_init(state_t *s, int flags)
{
LPDWORD lpChildId = NULL; /* child thread ID */
int i; /* for enumerating over bulk queues */
#ifdef SSWIN_DEBUG
fprintf(stderr, "SSWIN_DEBUG: TID%u, sound_win32_init()\n",
GetCurrentThreadId());
#endif
/* let app know what sound server we are running */
global_sound_server = &sound_server_win32;
/* register window class */
memset (&wndclass, 0, sizeof(WNDCLASS));
wndclass.lpfnWndProc = MainWndProc;
wndclass.hInstance = GetModuleHandle(NULL);
wndclass.lpszClassName = g_szMainWndClass;
if (!RegisterClass(&wndclass))
{
fprintf(stderr, "soundserver_win32.c: sound_win32_init(): can't
register class\n");
}
/* create our 'window' */
hWndSoundThread = CreateWindow (
g_szMainWndClass,
"FreeSCI Sound",
WS_DISABLED,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
NULL,
NULL,
NULL,
NULL
);
if (NULL == hWndSoundThread)
{
fprintf(stderr, "soundserver_win32.c: sound_win32_init_vidmode():
can't create window\n");
}
/* let app know what sound server we are running and yield to the scheduler */
sci_sched_yield();
/* store a copy of the master thread id so we can use it with our mutices
later */
master_thread_id = GetCurrentThreadId();
#ifdef SSWIN_DEBUG
fprintf(stderr, "SSWIN_DEBUG: TID%u - MASTER thread ID\n",
GetCurrentThreadId());
#endif
if (init_midi_device(s) < 0)
return -1;
if (flags & SOUNDSERVER_INIT_FLAG_REVERSE_STEREO)
reverse_stereo = 1;
/* create mutices and spawn thread */
out_mutex = CreateMutex(NULL,
FALSE, /* no thread gets the
mutex to start with */
out_mutex_name);
if (out_mutex == NULL)
{
fprintf(stderr, "sound_win32_init(): CreateMutex(%s) failed\n",
out_mutex_name);
}
in_mutex = CreateMutex(NULL, FALSE, in_mutex_name);
if (in_mutex == NULL)
{
fprintf(stderr, "sound_win32_init(): CreateMutex(%s) failed\n",
in_mutex_name);
}
/* set up name for bulk mutices and create them */
mutex_name_len = strlen(bulk_mutex_name) + 2; /* room for number and \0 */
mutex_name_temp = (char *) sci_malloc(mutex_name_len);
mutex_name_temp = strcpy(mutex_name_temp, bulk_mutex_name);
dump = (char *) sci_malloc(2);
for (i = 0; i < 2; i++) {
itoa(i, dump, 10);
strcat(mutex_name_temp, dump);
sci_init_queue(&(bulk_queues[i]));
bulk_mutices[i] = CreateMutex(NULL, FALSE, mutex_name_temp);
if (bulk_mutices[i] == NULL)
{
fprintf(stderr, "sound_win32_init(): CreateMutex(%s)
failed\n", mutex_name_temp);
}
mutex_name_temp[mutex_name_len - 2] = '\0'; /* overwrite number */
}
ds = stderr;
sound_eq_init(&inqueue);
sound_eq_init(&ev_queue);
child_thread = CreateThread( NULL, 0, win32_soundserver_init, s, 0, lpChildId);
if (child_thread == NULL)
{
fprintf(stderr, "sound_win32_init(): CreateThread() failed\n");
}
#if(_WIN32_WINNT >= 0x0400)
SetThreadIdealProcessor(child_thread, 1);
#endif
return 0;
}
int
sound_win32_configure(state_t *s, char *option, char *value)
{
#ifdef SSWIN_DEBUG
fprintf(stderr, "SSWIN_DEBUG: TID%u, sound_win32_configure()\n",
GetCurrentThreadId());
#endif
return 1; /* No options apply to this driver */
}
sound_event_t *
sound_win32_get_event(state_t *s)
{
sound_event_t *event = NULL;
#ifdef SSWIN_DEBUG
fprintf(stderr, "SSWIN_DEBUG: TID%u, sound_win32_get_event()\n",
GetCurrentThreadId());
#endif
if (WaitForSingleObject(out_mutex, MUTEX_TIMEOUT) != WAIT_OBJECT_0)
{
fprintf(stderr, "sound_win32_get_event(): WaitForSingleObject(%s)
failed\n", out_mutex_name);
return NULL;
}
if (!sound_eq_peek_event(&ev_queue))
{
if (ReleaseMutex(out_mutex) == 0)
{
fprintf(stderr, "sound_win32_get_event(): ReleaseMutex(%s)
failed\n", out_mutex_name);
}
return NULL;
}
event = sound_eq_retreive_event(&ev_queue);
if (ReleaseMutex(out_mutex) == 0)
{
fprintf(stderr, "sound_win32_get_event(): ReleaseMutex(%s) failed\n",
out_mutex_name);
}
/*
if (event)
printf("got %04x %d %d\n", event->handle, event->signal, event->value);
*/
return event;
}
void
sound_win32_queue_event(int handle, int signal, int value)
{
#ifdef SSWIN_DEBUG
fprintf(stderr, "SSWIN_DEBUG: TID%u, sound_win32_queue_event()\n",
GetCurrentThreadId());
#endif
if (WaitForSingleObject(out_mutex, MUTEX_TIMEOUT) != WAIT_OBJECT_0)
{
fprintf(stderr, "sound_win32_queue_event(): WaitForSingleObject(%s)
failed\n", out_mutex_name);
return;
}
sound_eq_queue_event(&ev_queue, handle, signal, value);
if (ReleaseMutex(out_mutex) == 0)
{
fprintf(stderr, "sound_win32_queue_event(): ReleaseMutex(%s)
failed\n", out_mutex_name);
}
/* printf("set %04x %d %d\n", handle, signal, value); */
}
void
sound_win32_queue_command(int handle, int signal, int value)
{
#ifdef SSWIN_DEBUG
fprintf(stderr, "SSWIN_DEBUG: TID%u, sound_win32_queue_command()\n",
GetCurrentThreadId());
#endif
if (WaitForSingleObject(in_mutex, MUTEX_TIMEOUT) != WAIT_OBJECT_0)
{
fprintf(stderr, "sound_win32_queue_command(): WaitForSingleObject(%s)
failed\n", in_mutex_name);
return;
}
sound_eq_queue_event(&inqueue, handle, signal, value);
if (ReleaseMutex(in_mutex) == 0)
{
fprintf(stderr, "sound_win32_queue_command(): ReleaseMutex(%s)
failed\n", in_mutex_name);
}
}
sound_event_t *
sound_win32_get_command(GTimeVal *wait_tvp)
{
sound_event_t *event = NULL;
#ifdef SSWIN_DEBUG
fprintf(stderr, "SSWIN_DEBUG: TID%u, sound_win32_get_command()\n",
GetCurrentThreadId());
#endif
if (WaitForSingleObject(in_mutex, MUTEX_TIMEOUT) != WAIT_OBJECT_0)
{
fprintf(stderr, "sound_win32_get_command(): WaitForSingleObject(%s)
failed\n", in_mutex_name);
return NULL;
}
if (!sound_eq_peek_event(&inqueue)) {
Sleep(1);
if (ReleaseMutex(in_mutex) == 0)
{
fprintf(stderr, "sound_win32_get_command(): ReleaseMutex(%s)
failed\n", in_mutex_name);
}
return NULL;
}
event = sound_eq_retreive_event(&inqueue);
if (ReleaseMutex(in_mutex) == 0)
{
fprintf(stderr, "sound_win32_get_command(): ReleaseMutex(%s)
failed\n", in_mutex_name);
}
return event;
}
int
sound_win32_get_data(byte **data_ptr, int *size, int maxlen)
{
int index = (GetCurrentThreadId() == master_thread_id);
void *data = NULL;
#ifdef SSWIN_DEBUG
fprintf(stderr, "SSWIN_DEBUG: TID%u, sound_win32_get_data()\n",
GetCurrentThreadId());
#endif
/* set up bulk mutex name */
itoa(index, dump, 10);
mutex_name_temp[mutex_name_len - 2] = '\0'; /* overwrite number there */
strcat(mutex_name_temp, dump);
if (WaitForSingleObject(bulk_mutices[index], MUTEX_TIMEOUT) != WAIT_OBJECT_0)
{
fprintf(stderr, "sound_win32_get_data(): WaitForSingleObject(%s)
failed\n", mutex_name_temp);
return -1;
}
while (!(data = sci_get_from_queue(&(bulk_queues[index]), size)))
{
/* no data, so release the mutex */
if (ReleaseMutex(bulk_mutices[index]) == 0)
{
fprintf(stderr, "sound_win32_get_data(): ReleaseMutex(%s)
failed\n", mutex_name_temp);
}
Sleep(1); /* take a nap */
/* then get it back again */
if (WaitForSingleObject(bulk_mutices[index], MUTEX_TIMEOUT) !=
WAIT_OBJECT_0)
{
fprintf(stderr, "sound_win32_get_data():
WaitForSingleObject(%s) failed\n", mutex_name_temp);
return -1;
}
}
if (ReleaseMutex(bulk_mutices[index]) == 0)
{
fprintf(stderr, "sound_win32_get_data(): ReleaseMutex(%s) failed\n",
mutex_name_temp);
}
*data_ptr = (byte *) data;
return *size;
}
int
sound_win32_send_data(byte *data_ptr, int maxsend)
{
int index = 1 - (GetCurrentThreadId() == master_thread_id);
#ifdef SSWIN_DEBUG
fprintf(stderr, "SSWIN_DEBUG: TID%u, sound_win32_send_data()\n",
GetCurrentThreadId());
#endif
/* set up bulk mutex name */
itoa(index, dump, 10);
mutex_name_temp[mutex_name_len - 2] = '\0'; /* overwrite number there */
strcat(mutex_name_temp, dump);
if (WaitForSingleObject(bulk_mutices[index], MUTEX_TIMEOUT) != WAIT_OBJECT_0)
{
fprintf(stderr, "sound_win32_send_data(): WaitForSingleObject(%s)
failed\n", mutex_name_temp);
return -1;
}
sci_add_to_queue(&(bulk_queues[index]), sci_memdup(data_ptr, maxsend),
maxsend);
if (ReleaseMutex(bulk_mutices[index]) == 0)
{
fprintf(stderr, "sound_win32_send_data(): ReleaseMutex(%s) failed\n",
mutex_name_temp);
}
return maxsend;
}
void
sound_win32_exit(state_t *s)
{
int i;
#ifdef SSWIN_DEBUG
fprintf(stderr, "SSWIN_DEBUG: TID%u, sound_win32_exit()\n",
GetCurrentThreadId());
#endif
sound_command(s, SOUND_COMMAND_SHUTDOWN, 0, 0); /* Kill server */
/* clean up */
WaitForSingleObject(child_thread, INFINITE);
CloseHandle(child_thread);
CloseHandle(out_mutex);
CloseHandle(in_mutex);
for (i = 0; i < 2; i++) {
void *data = NULL;
CloseHandle(bulk_mutices[i]);
while (data = sci_get_from_queue(&(bulk_queues[i]), NULL))
free(data); /* Flush queues */
}
free(mutex_name_temp);
free(dump);
}
int
sound_win32_save(state_t *s, char *dir)
{
int *success = NULL;
int retval;
int size;
#ifdef SSWIN_DEBUG
fprintf(stderr, "SSWIN_DEBUG: TID%u, sound_win32_save()\n",
GetCurrentThreadId());
#endif
/* we ignore the dir */
sound_command(s, SOUND_COMMAND_SAVE_STATE, 0, 2);
sound_send_data((byte *) ".", 2);
sound_get_data((byte **) &success, &size, sizeof(int));
retval = *success;
free(success);
return retval;
}
sound_server_t sound_server_win32 = {
"win32",
"0.1",
0,
&sound_win32_init,
&sound_win32_configure,
&sound_win32_exit,
&sound_win32_get_event,
&sound_win32_queue_event,
&sound_win32_get_command,
&sound_win32_queue_command,
&sound_win32_get_data,
&sound_win32_send_data,
&sound_win32_save,
&sound_restore,
&sound_command,
&sound_suspend,
&sound_resume
};
#endif