Don't allow multiple playaiff() threads.
authorDavid Griffith <dave@661.org>
Wed, 13 May 2015 21:03:19 +0000 (14:03 -0700)
committerDavid Griffith <dave@661.org>
Wed, 13 May 2015 21:03:19 +0000 (14:03 -0700)
If there's already a playaiff() thread going when another playaiff()
thread is to be started, tell the first one to stop and clean up, then
join to it when it terminates before proceeding to start another
playaiff() thread.  This will cleanly handle a barrage of samples called
to start in quick succession.  For example, suppose your game has
several buttons that make noises.  Doing "PRESS GREEN BUTTON. PRESS BLUE
BUTTON. PRESS RED BUTTON. PRESS YELLOW BUTTON" will not cause playaiff()
to trip over itself.  Instead all the samples will be played briefly
except the one for the yellow button, which will play until the player
does something else noisy at the next command prompt.  What's actually
heard is the last button noise.  Such a reaction probably isn't
desirable, but is well-within the responsibility of the game author to
decide if and how AIFF samples will be allowed to stomp on one another.
The business about allowing only one AIFF at a time and one OGG/MOD at a
time, but mix the two comes from the fact that all other Z-machine
emulators with audio capability behave this way.  The Z-machine spec
clearly says that only one sample (AIFF) may be played at once.  I don't
know where the idea that allowing an OGG/MOD to be mixed came from or
who decided it was okay.  The Blorb specs seem silent on the issue of
having multiple AIFFs and OGG/MODs playing at once.

src/curses/ux_audio.c

index cf248dc2b66587a02abcf84830b563d2010b866e..e2994525e79bbede0c42f83ff18acb1c909bbff5 100644 (file)
@@ -185,9 +185,12 @@ void os_start_sample (int number, int volume, int repeats, zword eos)
     myeffect.number = number;
 
     pthread_attr_init(&attr);
-    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
 
     if (blorb_map->chunks[resource.chunknum].type == bb_make_id('F','O','R','M')) {
+       if (bleep_playing) {
+           bleep_playing = FALSE;
+           pthread_join(playaiff_id, NULL);
+       }
        err = pthread_create(&playaiff_id, &attr, (void *) &playaiff, &myeffect);
        if (err != 0) {
            printf("Can't create playaiff thread :[%s]", strerror(err));
@@ -212,8 +215,9 @@ void os_start_sample (int number, int volume, int repeats, zword eos)
  */
 void os_stop_sample (int number)
 {
-    if (bleep_playing && number == bleepnum) {
-       bleep_stop = TRUE;
+    if (bleep_playing && (number == bleepnum || number == 0)) {
+       bleep_playing = FALSE;
+       while(pthread_kill(playaiff_id, 0) == 0);
     }
     return;
 }/* os_stop_sample */
@@ -448,7 +452,7 @@ void *playaiff(EFFECT *raw_effect)
 
     while (1) {
        /* Check if we're being told to stop. */
-       if (bleep_stop) break;
+       if (!bleep_playing) break;
        sem_wait(&audio_empty);
        pthread_mutex_lock(&mutex);
 
@@ -504,13 +508,14 @@ void *playaiff(EFFECT *raw_effect)
      * Whichever, we need to clean up and terminate this thread.
      */
 
-    bleep_stop = FALSE;
     bleep_playing = FALSE;
 
     fseek(myeffect.fp, filestart, SEEK_SET);
     sf_close(sndfile);
+    free(floatbuffer);
+    free(floatbuffer2);
 
-    pthread_exit((void*) raw_effect);
+    pthread_exit(NULL);
 }