Add Separate Buffers For Music/FX
authorfundamental <mark.d.mccurry@gmail.com>
Tue, 21 Feb 2017 15:29:22 +0000 (10:29 -0500)
committerfundamental <mark.d.mccurry@gmail.com>
Tue, 21 Feb 2017 15:29:22 +0000 (10:29 -0500)
This commit:
- Adds a second set of thread signaling semaphores
- Removes pthread_kill() to avoid one lock/unlock race
- Uses *equal length* samples from music/effect streams when mixing
- Temporarily adds a lot of debug prints

The threading logic for this code was rather poor prior to this commit.
As such, I make no claims to the correctness of the threading code
within this commit as the assumptions presented by the code previously
installed in the audio engine are not clearly stated.

The code as presented fixes some of the issues involved in mixing, however
effect + ogg music still seems to pose an issue and the reasons why are
unclear. A rewrite of the architecture of the audio engine is recommended.

src/curses/ux_audio.c

index 1914f26f845b478bc035005d839b7ad8c5bf45a7..d7c1857e48d52cef26e32fe518493cd122f93df2 100644 (file)
@@ -82,24 +82,37 @@ static pthread_t    mixer_id;
 static pthread_t       playaiff_id;
 static pthread_t       playmusic_id;
 static pthread_mutex_t mutex;
-static sem_t           audio_full;
-static sem_t           audio_empty;
 static sem_t           playaiff_okay;
 static sem_t           playmusic_okay;
 
 bool    bleep_playing = FALSE;
 bool   bleep_stop = FALSE;
 
-float  *bleepbuffer;
-int    bleepsamples;
 int    bleepcount;
 int    bleepnum;
 
 bool    music_playing = FALSE;
 bool   music_stop = FALSE;
 
-float  *musicbuffer;
-int    musicsamples;
+typedef struct
+{
+    sem_t   full;
+    sem_t   empty;
+    float  *samples;
+    int     nsamples;
+} audiobuffer;
+
+audiobuffer bleep_buffer;
+audiobuffer music_buffer;
+
+void audiobuffer_init(audiobuffer *ab)
+{
+    sem_init(&ab->full, 0, 0);
+    sem_init(&ab->empty, 0, 0);
+    sem_post(&ab->empty);
+    ab->samples = malloc(BUFFSIZE * 2 * sizeof(float));
+    ab->nsamples = 0;
+}
 
 
 /*
@@ -115,23 +128,11 @@ void os_init_sound(void)
     static pthread_attr_t attr;
 
     pthread_mutex_init(&mutex, NULL);
-    sem_init(&audio_empty, 0, 1);
-    sem_init(&audio_full, 0, 0);
+    audiobuffer_init(&music_buffer);
+    audiobuffer_init(&bleep_buffer);
     sem_init(&playaiff_okay, 0, 0);
     sem_init(&playmusic_okay, 0, 0);
 
-    musicbuffer = malloc(BUFFSIZE * 2 * sizeof(float));
-    if (musicbuffer == NULL) {
-       printf("Unable to malloc musicbuffer\n");
-       exit(1);
-    }
-
-    bleepbuffer = malloc(BUFFSIZE * 2 * sizeof(float));
-    if (bleepbuffer == NULL) {
-       printf("Unable to malloc bleepbuffer\n");
-       exit(1);
-    }
-
     pthread_attr_init(&attr);
     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
 
@@ -255,13 +256,15 @@ void os_start_sample (int number, int volume, int repeats, zword eos)
 void os_stop_sample (int number)
 {
     if (bleep_playing && (number == bleepnum || number == 0)) {
-       bleep_playing = FALSE;
-       while(pthread_kill(playaiff_id, 0) == 0);
+        bleep_playing = FALSE;
+        sem_post(&bleep_buffer.empty);
+        pthread_join(playaiff_id, 0);
     }
 
     if (get_music_playing() && (number == get_musicnum () || number == 0)) {
-       set_music_playing(false);
-       while(pthread_kill(playmusic_id, 0) == 0);
+        set_music_playing(false);
+        sem_post(&music_buffer.empty);
+        pthread_join(playmusic_id, 0);
     }
 
     return;
@@ -326,8 +329,8 @@ static void *mixer(void * UNUSED(arg))
 
     shortbuffer = malloc(BUFFSIZE * sizeof(short) * 2);
     if (shortbuffer == NULL) {
-       printf("Unable to malloc shortbuffer\n");
-       exit(1);
+        printf("Unable to malloc shortbuffer\n");
+        exit(1);
     }
 
     memset(&format, 0, sizeof(ao_sample_format));
@@ -340,42 +343,138 @@ static void *mixer(void * UNUSED(arg))
     device = NULL;
 
     while (1) {
-        sem_wait(&audio_full);          /* Wait until output buffer is full */
+        if(music_playing) {
+            FILE *f= fopen("log_file.txt", "a+");
+            fprintf(f, "MUSIC WAIT\n");
+            fclose(f);
+            sem_wait(&music_buffer.full);          /* Wait until output buffer is full */
+        }
+        if(bleep_playing ) {
+            FILE *f= fopen("log_file.txt", "a+");
+            fprintf(f, "BLEEP WAIT\n");
+            fclose(f);
+            sem_wait(&bleep_buffer.full);          /* Wait until output buffer is full */
+        }
+        FILE *f= fopen("log_file.txt", "a+");
+        fprintf(f, "Loop...\n");
+        fclose(f);
+
         pthread_mutex_lock(&mutex);     /* Acquire mutex */
 
-       if (device == NULL) {
-           device = ao_open_live(default_driver, &format, NULL);
-           if (device == NULL) {
-               printf(" Error opening sound device.\n");
-           }
-       }
+        if (device == NULL) {
+            device = ao_open_live(default_driver, &format, NULL);
+            if (device == NULL) {
+                printf(" Error opening sound device.\n");
+            }
+        }
 
-       if (bleep_playing && !music_playing) {
-           floattopcm16(shortbuffer, bleepbuffer, bleepsamples);
-            ao_play(device, (char *) shortbuffer, bleepsamples * sizeof(short));
-       }
+        if (bleep_playing && !music_playing) {
+            FILE *f= fopen("log_file.txt", "a+");
+            fprintf(f, "BLEEP: %d\n", bleep_buffer.nsamples);
+            fclose(f);
+            floattopcm16(shortbuffer, bleep_buffer.samples, bleep_buffer.nsamples);
+            ao_play(device, (char *) shortbuffer, bleep_buffer.nsamples * sizeof(short));
+            bleep_buffer.nsamples = 0;
+        }
 
-       if (music_playing && !bleep_playing) {
-           floattopcm16(shortbuffer, musicbuffer, musicsamples);
-            ao_play(device, (char *) shortbuffer, musicsamples * sizeof(short));
-       }
+        if (music_playing && !bleep_playing) {
+            FILE *f= fopen("log_file.txt", "a+");
+            fprintf(f, "MUSIC: %d\n", music_buffer.nsamples);
+            fclose(f);
+            floattopcm16(shortbuffer, music_buffer.samples, music_buffer.nsamples);
+            ao_play(device, (char *) shortbuffer, music_buffer.nsamples * sizeof(short));
+            music_buffer.nsamples = 0;
+        }
 
-       if (music_playing && bleep_playing) {
-           for (i = 0; i < BUFFSIZE; i++) {
-               bleepbuffer[i] += musicbuffer[i];
-           }
-           samplecount = musicsamples > bleepsamples ? musicsamples : bleepsamples;
-           floattopcm16(shortbuffer, bleepbuffer, samplecount);
-           ao_play(device, (char *) shortbuffer, samplecount * sizeof(short));
-       }
+        if (music_playing && bleep_playing) {
+            {
+            FILE *f= fopen("log_file.txt", "a+");
+            fprintf(f, "COMBO [start]: %d vs %d\n", music_buffer.nsamples, bleep_buffer.nsamples);
+            fclose(f);
+            }
+            int samples = 100000;
+            if(bleep_buffer.nsamples == -1)
+                bleep_buffer.nsamples = 0;
+            if(music_buffer.nsamples == -1)
+                music_buffer.nsamples = 0;
+            if(samples > bleep_buffer.nsamples && bleep_buffer.nsamples > 0);
+                samples = bleep_buffer.nsamples;
+
+            if(samples > music_buffer.nsamples && music_buffer.nsamples > 0)
+                samples = music_buffer.nsamples;
+
+            float *outbuf = calloc(samples+1,sizeof(float));
+            for(int i=0; i < samples; ++i)
+                outbuf[i] += music_buffer.samples[i];
+            for(int i=0; i < samples; ++i)
+                outbuf[i] += bleep_buffer.samples[i];
+
+            //only partially consume data
+            if(bleep_buffer.nsamples > samples) {
+                memmove(bleep_buffer.samples, bleep_buffer.samples+samples,
+                        sizeof(float)*(bleep_buffer.nsamples-samples));
+            }
+            if(bleep_buffer.nsamples > 0)
+                bleep_buffer.nsamples -= samples;
+
+            if(music_buffer.nsamples > samples) {
+                memmove(music_buffer.samples, music_buffer.samples+samples,
+                        sizeof(float)*(music_buffer.nsamples-samples));
+            }
+            if(music_buffer.nsamples > 0)
+                music_buffer.nsamples -= samples;
+
+
+            {
+            FILE *f= fopen("log_file.txt", "a+");
+            fprintf(f, "COMBO [end]: %d vs %d\n", music_buffer.nsamples, bleep_buffer.nsamples);
+            fclose(f);
+            }
+
+
+            samplecount = samples;
+            floattopcm16(shortbuffer, outbuf, samples);
+            ao_play(device, (char *) shortbuffer, samplecount * sizeof(short));
+            free(outbuf);
+        }
 
-       if (!bleep_playing && !music_playing) {
-           ao_close(device);
-           device = NULL;
-       }
+        if (!bleep_playing && !music_playing) {
+            ao_close(device);
+            device = NULL;
+        }
 
         pthread_mutex_unlock(&mutex);   /* release the mutex lock */
-        sem_post(&audio_empty);         /* signal empty */
+
+        if(bleep_buffer.nsamples) {
+            FILE *f= fopen("log_file.txt", "a+");
+            fprintf(f, "BLEEP STILL FULL\n", music_buffer.nsamples, bleep_buffer.nsamples);
+            fclose(f);
+            sem_post(&bleep_buffer.full);
+        }
+        if(music_buffer.nsamples) {
+            sem_post(&music_buffer.full);
+            FILE *f= fopen("log_file.txt", "a+");
+            fprintf(f, "MUSIC STILL FULL\n", music_buffer.nsamples, bleep_buffer.nsamples);
+            fclose(f);
+        }
+
+        int tmp;
+        sem_getvalue(&bleep_buffer.empty, &tmp);
+
+        if(bleep_buffer.nsamples <= 0 && tmp == 0) {
+            sem_post(&bleep_buffer.empty);         /* signal empty */
+            FILE *f= fopen("log_file.txt", "a+");
+            fprintf(f, "BLEEP POST\n", music_buffer.nsamples, bleep_buffer.nsamples);
+            fclose(f);
+        }
+
+        sem_getvalue(&music_buffer.empty, &tmp);
+        if(music_buffer.nsamples <= 0 && tmp == 0) {
+            sem_post(&music_buffer.empty);         /* signal empty */
+            FILE *f= fopen("log_file.txt", "a+");
+            fprintf(f, "MUSIC POST\n", music_buffer.nsamples, bleep_buffer.nsamples);
+            fclose(f);
+        }
     }
 } /* mixer */
 
@@ -495,7 +594,7 @@ void *playaiff(EFFECT *raw_effect)
 
     floatbuffer = malloc(BUFFSIZE * sf_info.channels * sizeof(float));
     floatbuffer2 = malloc(BUFFSIZE * 2 * sizeof(float));
-    memset(bleepbuffer, 0, BUFFSIZE * sizeof(float) * 2);
+    memset(bleep_buffer.samples, 0, BUFFSIZE * sizeof(float) * 2);
 
     /* Set up for conversion */
     if ((src_state = src_new(SRC_SINC_FASTEST, sf_info.channels, &error)) == NULL) {
@@ -512,56 +611,59 @@ void *playaiff(EFFECT *raw_effect)
     bleep_playing = TRUE;
 
     while (1) {
-       /* Check if we're being told to stop. */
-       if (!bleep_playing) break;
-       sem_wait(&audio_empty);
-       pthread_mutex_lock(&mutex);
-
-       /* If floatbuffer is empty, refill it. */
-       if (src_data.input_frames == 0) {
-           src_data.input_frames = sf_readf_float(sndfile, floatbuffer, BUFFSIZE / sf_info.channels);
-           src_data.data_in = floatbuffer;
-           /* Mark end of input. */
-           if (src_data.input_frames < BUFFSIZE / sf_info.channels)
-               src_data.end_of_input = SF_TRUE;
-       }
-
-       /* Do the sample rate conversion. */
-       if ((error = src_process(src_state, &src_data))) {
-           printf("Error: %s\n", src_strerror(error));
-           exit(1);
-       }
+        /* Check if we're being told to stop. */
+        if (!bleep_playing) break;
+        sem_wait(&bleep_buffer.empty);
+        pthread_mutex_lock(&mutex);
+
+        /* If floatbuffer is empty, refill it. */
+        if (src_data.input_frames == 0) {
+            src_data.input_frames = sf_readf_float(sndfile, floatbuffer, BUFFSIZE / sf_info.channels);
+            src_data.data_in = floatbuffer;
+            /* Mark end of input. */
+            if (src_data.input_frames < BUFFSIZE / sf_info.channels)
+                src_data.end_of_input = SF_TRUE;
+        }
 
-       bleepsamples = src_data.output_frames_gen * 2;
+        /* Do the sample rate conversion. */
+        if ((error = src_process(src_state, &src_data))) {
+            printf("Error: %s\n", src_strerror(error));
+            exit(1);
+        }
 
-       /* Stereoize monaural sound-effects. */
-       if (sf_info.channels == 1) {
-           /* Remember that each monaural frame contains just one sample. */
-           stereoize(bleepbuffer, floatbuffer2, src_data.output_frames_gen);
-       } else {
-           /* It's already stereo.  Just copy the buffer. */
-           memcpy(bleepbuffer, floatbuffer2, sizeof(float) * src_data.output_frames_gen * 2);
-       }
+        bleep_buffer.nsamples = src_data.output_frames_gen * 2;
+        FILE *f= fopen("log_file.txt", "a+");
+        fprintf(f, "Computed BLEEP: %d\n", bleep_buffer.nsamples);
+        fclose(f);
+
+        /* Stereoize monaural sound-effects. */
+        if (sf_info.channels == 1) {
+            /* Remember that each monaural frame contains just one sample. */
+            stereoize(bleep_buffer.samples, floatbuffer2, src_data.output_frames_gen);
+        } else {
+            /* It's already stereo.  Just copy the buffer. */
+            memcpy(bleep_buffer.samples, floatbuffer2, sizeof(float) * src_data.output_frames_gen * 2);
+        }
 
-       /* Adjust volume. */
-       for (volcount = 0; volcount <= bleepsamples; volcount++)
-           bleepbuffer[volcount] /= volfactor;
+        /* Adjust volume. */
+        for (volcount = 0; volcount <= bleep_buffer.nsamples; volcount++)
+            bleep_buffer.samples[volcount] /= volfactor;
 
-       /* If that's all, terminate and signal that we're done. */
-       if (src_data.end_of_input && src_data.output_frames_gen == 0) {
-           sem_post(&audio_full);
-           pthread_mutex_unlock(&mutex);
-           break;
-       }
+        /* If that's all, terminate and signal that we're done. */
+        if (src_data.end_of_input && src_data.output_frames_gen == 0) {
+            sem_post(&bleep_buffer.full);
+            pthread_mutex_unlock(&mutex);
+            break;
+        }
 
-       /* Get ready for the next chunk. */
+        /* Get ready for the next chunk. */
         output_count += src_data.output_frames_gen;
         src_data.data_in += src_data.input_frames_used * sf_info.channels;
         src_data.input_frames -= src_data.input_frames_used;
 
-       /* By this time, the buffer is full.  Signal the mixer to play it. */
-       pthread_mutex_unlock(&mutex);
-       sem_post(&audio_full);
+        /* By this time, the buffer is full.  Signal the mixer to play it. */
+        pthread_mutex_unlock(&mutex);
+        sem_post(&bleep_buffer.full);
     }
 
     /* The two ways to exit the above loop are to process all the
@@ -570,12 +672,12 @@ void *playaiff(EFFECT *raw_effect)
      */
 
     bleep_playing = FALSE;
-    memset(bleepbuffer, 0, BUFFSIZE * sizeof(float) * 2);
+    memset(bleep_buffer.samples, 0, BUFFSIZE * sizeof(float) * 2);
 
-//    fseek(myeffect.fp, filestart, SEEK_SET);
+    //    fseek(myeffect.fp, filestart, SEEK_SET);
 
-//    pthread_mutex_unlock(&mutex);
-//    sem_post(&audio_empty);
+    //    pthread_mutex_unlock(&mutex);
+    //    sem_post(&audio_empty);
 
     sf_close(sndfile);
     free(floatbuffer);
@@ -655,8 +757,8 @@ static void *playmod(EFFECT *raw_effect)
     mod = ModPlug_Load(filedata, size);
     fseek(myeffect.fp, filestart, SEEK_SET);
     if (!mod) {
-       printf("Unable to load MOD chunk.\n\r");
-       return 0;
+        printf("Unable to load MOD chunk.\n\r");
+        return 0;
     }
 
     if (myeffect.vol < 1) myeffect.vol = 1;
@@ -668,24 +770,27 @@ static void *playmod(EFFECT *raw_effect)
     music_playing = TRUE;
 
     while (1) {
-       sem_wait(&audio_empty);
-       pthread_mutex_lock(&mutex);
-       memset(musicbuffer, 0, BUFFSIZE * sizeof(float) * 2);
-       if (!music_playing) {
-           break;
-       }
-       musicsamples = ModPlug_Read(mod, shortbuffer, BUFFSIZE) / 2;
-       pcm16tofloat(musicbuffer, shortbuffer, musicsamples);
-       if (musicsamples == 0) break;
-       pthread_mutex_unlock(&mutex);
-       sem_post(&audio_full);
+        sem_wait(&music_buffer.empty);
+        pthread_mutex_lock(&mutex);
+        memset(music_buffer.samples, 0, BUFFSIZE * sizeof(float) * 2);
+        if (!music_playing) {
+            break;
+        }
+        music_buffer.nsamples = ModPlug_Read(mod, shortbuffer, BUFFSIZE) / 2;
+        FILE *f= fopen("log_file.txt", "a+");
+        fprintf(f, "Computed Mod: %d\n", bleep_buffer.nsamples);
+        fclose(f);
+        pcm16tofloat(music_buffer.samples, shortbuffer, music_buffer.nsamples);
+        if (music_buffer.nsamples == 0) break;
+        pthread_mutex_unlock(&mutex);
+        sem_post(&music_buffer.full);
     }
 
     music_playing = FALSE;
-    memset(musicbuffer, 0, BUFFSIZE * sizeof(float) * 2);
+    memset(music_buffer.samples, 0, BUFFSIZE * sizeof(float) * 2);
 
     pthread_mutex_unlock(&mutex);
-    sem_post(&audio_empty);
+    sem_post(&music_buffer.empty);
 
     ModPlug_Unload(mod);
     free(shortbuffer);
@@ -777,30 +882,36 @@ static void *playogg(EFFECT *raw_effect)
     music_playing = TRUE;
 
     while (count < toread) {
-       sem_wait(&audio_empty);
+       sem_wait(&music_buffer.empty);
        pthread_mutex_lock(&mutex);
-       memset(musicbuffer, 0, BUFFSIZE * sizeof(float) * 2);
+       memset(music_buffer.samples, 0, BUFFSIZE * sizeof(float) * 2);
        if (!music_playing) break;
 
         frames_read = ov_read(&vf, (char *)shortbuffer, BUFFSIZE, 0,2,1,&current_section);
 
-        pcm16tofloat(musicbuffer, shortbuffer, frames_read);
+        pcm16tofloat(music_buffer.samples, shortbuffer, frames_read);
         for (volcount = 0; volcount <= frames_read / 2; volcount++) {
-            ((float *) musicbuffer)[volcount] /= volfactor;
+            ((float *) music_buffer.samples)[volcount] /= volfactor;
         }
 
-       musicsamples = frames_read / 2;
-        count += frames_read;
+       music_buffer.nsamples  = frames_read / 2;
+    if(music_buffer.nsamples == -1)
+        music_buffer.nsamples  = 0;
+    //perform mix down
+    FILE *f= fopen("log_file.txt", "a+");
+    fprintf(f, "Computed ogg: %d\n", music_buffer.nsamples);
+    fclose(f);
+    count += frames_read;
 
        pthread_mutex_unlock(&mutex);
-       sem_post(&audio_full);
+       sem_post(&music_buffer.full);
     }
 
 //    fseek(myeffect.fp, filestart, SEEK_SET);
     music_playing = FALSE;
 
     pthread_mutex_unlock(&mutex);
-    sem_post(&audio_empty);
+    sem_post(&music_buffer.empty);
 
     ov_clear(&vf);