Guest User

Mix audio stream with libsndfile and libao

a guest
May 17th, 2016
96
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2. ** gcc -o mix mix.c -lao -lsndfile -lm -lpthread
  3. **
  4. */
  5.  
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <stdint.h>
  9. #include <string.h>
  10. #include <math.h>
  11. #include <ao/ao.h>
  12. #include <sndfile.h>
  13. #include <pthread.h>
  14.  
  15. #define BUFFSIZE 512
  16.  
  17. typedef struct _sound {
  18.     SNDFILE *file;
  19.     SF_INFO *info;
  20.     float gain;
  21. } Sound;
  22.  
  23. typedef struct _play_item PlayItem;
  24.  
  25. struct _play_item {
  26.     Sound *sound;
  27.     PlayItem *next;
  28. };
  29.  
  30. Sound *new_sound (const char *filename, float gain);
  31. void free_sound (Sound *sound);
  32. void play_sound (Sound *sound);
  33.  
  34. static PlayItem *new_play_item ();
  35. static void free_play_item (PlayItem *);
  36. static void queue_play_item (PlayItem *);
  37.  
  38. static void *mix (void *arg);
  39.  
  40. static PlayItem *play_queue = NULL;
  41. static volatile int mix_thread_exit_requested = 0;
  42. static pthread_mutex_t mix_mutex = PTHREAD_MUTEX_INITIALIZER;
  43.  
  44. int main(int argc, char *argv[])
  45. {
  46.     pthread_t mix_thread;
  47.     int sndc = (argc - 1);
  48.     char *table[sndc];
  49.  
  50.     if (argc < 2 || argc > 10) {
  51.         printf("usage: %s file1 file2 fileN ...\nN < 10\n", argv[0]);
  52.         exit(1);
  53.     }
  54.  
  55.     printf ("\nadding files:\n");
  56.     for(int i=0; i < sndc; i++) {
  57.         table[i] = argv[i + 1];
  58.         printf ("    %d - file %s\n", i + 1, table[i]);
  59.     }
  60.  
  61.     ao_initialize ();
  62.  
  63.     // create the mix thread
  64.     if(pthread_create(&mix_thread, NULL, &mix, NULL)) {
  65.         fprintf (stderr, "error creating mixing thread\n");
  66.         exit (-2);
  67.     }
  68.  
  69.     printf ("mixing thread started\n\n");
  70.     printf ("write a number between 1 and %d to play a file (Q or q to quit).\n", sndc);
  71.  
  72.     int c;  
  73.     while((c=getchar())!= EOF) {
  74.         if (c == 'Q' || c == 'q') {
  75.             printf ("request mixing thread to finish\n");
  76.             pthread_mutex_lock(&mix_mutex);
  77.             mix_thread_exit_requested = 1;
  78.             pthread_mutex_unlock(&mix_mutex);
  79.             break;
  80.         } else if (c > 48 && c < 57) {
  81.             c = c - 49;
  82.             if (c < sndc) {
  83.                 printf ("playing sound %s\n", table[c]);
  84.                 Sound *sound = new_sound (table[c], 1.0f / sndc);
  85.                 play_sound (sound);
  86.             } else {
  87.                 printf ("no sound at slot %d\n", c + 1);
  88.             }
  89.         } else if ( c != '\n') {
  90.             printf ("invalid char %c", c);
  91.         }
  92.     }
  93.  
  94.     printf ("waiting mixing thread to finish\n");
  95.  
  96.     if(pthread_join(mix_thread, NULL)) {
  97.         fprintf(stderr, "error joining mixing thread\n");
  98.         exit (-3);
  99.     }
  100.  
  101.     printf ("cleanup remaining play items\n");
  102.     PlayItem *current = play_queue;
  103.     while (current != NULL) {
  104.         PlayItem *tmp = current;
  105.         current = current->next;
  106.  
  107.         fprintf (stderr, "   * cleanup item %p\n",tmp);
  108.         free_sound (tmp->sound);
  109.         free_play_item (tmp);
  110.     }
  111.  
  112.     ao_shutdown ();
  113.  
  114.     printf ("bye\n");
  115.     return 0;
  116. }
  117.  
  118. Sound* new_sound (const char *filename, float gain) {
  119.     Sound *snd = malloc(sizeof(Sound));
  120.  
  121.     snd->gain = gain;
  122.     snd->info = malloc(sizeof(SF_INFO));
  123.     snd->info->format = 0;
  124.     snd->file = sf_open (filename, SFM_READ, snd->info);
  125.     if (snd->file == NULL) {
  126.         fprintf (stderr, "error opening '%s': %s\n", filename, sf_strerror (NULL));
  127.     }
  128.     return snd;
  129. }
  130.  
  131. void free_sound (Sound *sound) {
  132.     free (sound->info);
  133.     sf_close (sound->file);
  134.     free (sound);
  135. }
  136.  
  137. void play_sound (Sound *sound) {
  138.     PlayItem *item = new_play_item ();
  139.     item->sound = sound;
  140.  
  141.     queue_play_item (item);
  142. }
  143.  
  144. PlayItem *new_play_item () {
  145.     PlayItem *item = malloc(sizeof(PlayItem));
  146.     item->next = NULL;
  147.  
  148.     return item;
  149. }
  150.  
  151. void free_play_item (PlayItem *item) {
  152.     free (item);
  153. }
  154.  
  155. void queue_play_item (PlayItem *item) {
  156.     if (item == NULL)
  157.         return;
  158.  
  159.     fprintf (stderr, "   * queue item %p\n", item);
  160.     pthread_mutex_lock(&mix_mutex);
  161.     if (play_queue == NULL)
  162.         play_queue = item;
  163.     else
  164.         play_queue->next = item;
  165.  
  166.     pthread_mutex_unlock(&mix_mutex);
  167. }
  168.  
  169. /*
  170.  * Mixing function
  171.  *
  172.  * Limits:
  173.  *
  174.  *   No RT safe: use IO functions and mutex
  175.  *   Push model: better to use pull from the device driver (can I do this with libao?)
  176.  *   All the sample must be in the same format (both sample rate and channels)
  177.  *
  178.  */
  179. void *mix (void *arg)
  180. {
  181.     int default_driver;
  182.     ao_device *device;
  183.     ao_sample_format format;
  184.     float *filebuffer;
  185.     float *mixbuffer;
  186.     short *outbuffer;
  187.  
  188.     int buflen;
  189.     int toread = 0;
  190.  
  191.     // all files MUST be in the same format / channels
  192.     buflen = BUFFSIZE * 2;
  193.     filebuffer = malloc(buflen * sizeof(float));
  194.     mixbuffer= malloc(buflen * sizeof(float));
  195.     outbuffer= malloc(buflen * sizeof(short));
  196.  
  197.     default_driver = ao_default_driver_id();
  198.     memset(&format, 0, sizeof(ao_sample_format));
  199.  
  200.     format.byte_format = AO_FMT_NATIVE;
  201.     format.bits = 16;
  202.     format.channels = 2;
  203.     format.rate = 44100;
  204.  
  205.     device = ao_open_live(default_driver, &format, NULL);
  206.     if (device == NULL) {
  207.         printf("Error opening sound device.\n");
  208.         exit(-2);
  209.     }
  210.  
  211.     while (1) {
  212.  
  213.         pthread_mutex_lock(&mix_mutex);
  214.         int running = mix_thread_exit_requested == 0;
  215.         pthread_mutex_unlock(&mix_mutex);
  216.  
  217.         if (!running)
  218.             break;
  219.  
  220.         for(int item = 0; item < buflen; item++) {
  221.             mixbuffer[item] = 0;
  222.         }
  223.  
  224.         pthread_mutex_lock(&mix_mutex);
  225.  
  226.         PlayItem *current = play_queue;
  227.         PlayItem *previous = NULL;
  228.         while (current != NULL) {
  229.             sf_count_t item_read = sf_read_float (current->sound->file, filebuffer, BUFFSIZE); // WHY BUFFSIZE? Shouldn't be BUFFSIZE * channels?
  230.  
  231.             if (item_read > 0) {
  232.                 toread -= item_read;
  233.  
  234.                 // mix samples
  235.                 for(int item = 0; item < item_read; item++) {
  236.                     mixbuffer[item] += (filebuffer[item] * current->sound->gain);
  237.                 }
  238.                 previous = current;
  239.                 current = current->next;
  240.             } else {
  241.                // dequeue element
  242.                PlayItem *tmp = current;
  243.  
  244.                current = current->next;
  245.  
  246.                if (tmp == play_queue)
  247.                    play_queue = current;
  248.                else
  249.                    previous->next = current;
  250.  
  251.                fprintf (stderr, "   * dequeue item %p\n", tmp);
  252.                free_sound (tmp->sound);
  253.                free_play_item (tmp);
  254.             }
  255.         }
  256.         pthread_mutex_unlock(&mix_mutex);
  257.  
  258.         // convert from float to short
  259.         for(int item = 0; item < buflen; item++) {
  260.             int value = mixbuffer[item] * 32768;
  261.  
  262.             // limit to valid short values
  263.             if (value > 32767)
  264.                 value = 32767;
  265.             else if (value < -32768)
  266.                 value = -32768;
  267.  
  268.             outbuffer[item] =  (short) value;
  269.         }
  270.  
  271.         ao_play(device, (char *)outbuffer, buflen);
  272.     }
  273.  
  274.     ao_close(device);
  275.     free(filebuffer);
  276.     free(mixbuffer);
  277.     free(outbuffer);
  278.  
  279.     return NULL;
  280. }
Add Comment
Please, Sign In to add comment