Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- ** gcc -o mix mix.c -lao -lsndfile -lm -lpthread
- **
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdint.h>
- #include <string.h>
- #include <math.h>
- #include <ao/ao.h>
- #include <sndfile.h>
- #include <pthread.h>
- #define BUFFSIZE 512
- typedef struct _sound {
- SNDFILE *file;
- SF_INFO *info;
- float gain;
- } Sound;
- typedef struct _play_item PlayItem;
- struct _play_item {
- Sound *sound;
- PlayItem *next;
- };
- Sound *new_sound (const char *filename, float gain);
- void free_sound (Sound *sound);
- void play_sound (Sound *sound);
- static PlayItem *new_play_item ();
- static void free_play_item (PlayItem *);
- static void queue_play_item (PlayItem *);
- static void *mix (void *arg);
- static PlayItem *play_queue = NULL;
- static volatile int mix_thread_exit_requested = 0;
- static pthread_mutex_t mix_mutex = PTHREAD_MUTEX_INITIALIZER;
- int main(int argc, char *argv[])
- {
- pthread_t mix_thread;
- int sndc = (argc - 1);
- char *table[sndc];
- if (argc < 2 || argc > 10) {
- printf("usage: %s file1 file2 fileN ...\nN < 10\n", argv[0]);
- exit(1);
- }
- printf ("\nadding files:\n");
- for(int i=0; i < sndc; i++) {
- table[i] = argv[i + 1];
- printf (" %d - file %s\n", i + 1, table[i]);
- }
- ao_initialize ();
- // create the mix thread
- if(pthread_create(&mix_thread, NULL, &mix, NULL)) {
- fprintf (stderr, "error creating mixing thread\n");
- exit (-2);
- }
- printf ("mixing thread started\n\n");
- printf ("write a number between 1 and %d to play a file (Q or q to quit).\n", sndc);
- int c;
- while((c=getchar())!= EOF) {
- if (c == 'Q' || c == 'q') {
- printf ("request mixing thread to finish\n");
- pthread_mutex_lock(&mix_mutex);
- mix_thread_exit_requested = 1;
- pthread_mutex_unlock(&mix_mutex);
- break;
- } else if (c > 48 && c < 57) {
- c = c - 49;
- if (c < sndc) {
- printf ("playing sound %s\n", table[c]);
- Sound *sound = new_sound (table[c], 1.0f / sndc);
- play_sound (sound);
- } else {
- printf ("no sound at slot %d\n", c + 1);
- }
- } else if ( c != '\n') {
- printf ("invalid char %c", c);
- }
- }
- printf ("waiting mixing thread to finish\n");
- if(pthread_join(mix_thread, NULL)) {
- fprintf(stderr, "error joining mixing thread\n");
- exit (-3);
- }
- printf ("cleanup remaining play items\n");
- PlayItem *current = play_queue;
- while (current != NULL) {
- PlayItem *tmp = current;
- current = current->next;
- fprintf (stderr, " * cleanup item %p\n",tmp);
- free_sound (tmp->sound);
- free_play_item (tmp);
- }
- ao_shutdown ();
- printf ("bye\n");
- return 0;
- }
- Sound* new_sound (const char *filename, float gain) {
- Sound *snd = malloc(sizeof(Sound));
- snd->gain = gain;
- snd->info = malloc(sizeof(SF_INFO));
- snd->info->format = 0;
- snd->file = sf_open (filename, SFM_READ, snd->info);
- if (snd->file == NULL) {
- fprintf (stderr, "error opening '%s': %s\n", filename, sf_strerror (NULL));
- }
- return snd;
- }
- void free_sound (Sound *sound) {
- free (sound->info);
- sf_close (sound->file);
- free (sound);
- }
- void play_sound (Sound *sound) {
- PlayItem *item = new_play_item ();
- item->sound = sound;
- queue_play_item (item);
- }
- PlayItem *new_play_item () {
- PlayItem *item = malloc(sizeof(PlayItem));
- item->next = NULL;
- return item;
- }
- void free_play_item (PlayItem *item) {
- free (item);
- }
- void queue_play_item (PlayItem *item) {
- if (item == NULL)
- return;
- fprintf (stderr, " * queue item %p\n", item);
- pthread_mutex_lock(&mix_mutex);
- if (play_queue == NULL)
- play_queue = item;
- else
- play_queue->next = item;
- pthread_mutex_unlock(&mix_mutex);
- }
- /*
- * Mixing function
- *
- * Limits:
- *
- * No RT safe: use IO functions and mutex
- * Push model: better to use pull from the device driver (can I do this with libao?)
- * All the sample must be in the same format (both sample rate and channels)
- *
- */
- void *mix (void *arg)
- {
- int default_driver;
- ao_device *device;
- ao_sample_format format;
- float *filebuffer;
- float *mixbuffer;
- short *outbuffer;
- int buflen;
- int toread = 0;
- // all files MUST be in the same format / channels
- buflen = BUFFSIZE * 2;
- filebuffer = malloc(buflen * sizeof(float));
- mixbuffer= malloc(buflen * sizeof(float));
- outbuffer= malloc(buflen * sizeof(short));
- default_driver = ao_default_driver_id();
- memset(&format, 0, sizeof(ao_sample_format));
- format.byte_format = AO_FMT_NATIVE;
- format.bits = 16;
- format.channels = 2;
- format.rate = 44100;
- device = ao_open_live(default_driver, &format, NULL);
- if (device == NULL) {
- printf("Error opening sound device.\n");
- exit(-2);
- }
- while (1) {
- pthread_mutex_lock(&mix_mutex);
- int running = mix_thread_exit_requested == 0;
- pthread_mutex_unlock(&mix_mutex);
- if (!running)
- break;
- for(int item = 0; item < buflen; item++) {
- mixbuffer[item] = 0;
- }
- pthread_mutex_lock(&mix_mutex);
- PlayItem *current = play_queue;
- PlayItem *previous = NULL;
- while (current != NULL) {
- sf_count_t item_read = sf_read_float (current->sound->file, filebuffer, BUFFSIZE); // WHY BUFFSIZE? Shouldn't be BUFFSIZE * channels?
- if (item_read > 0) {
- toread -= item_read;
- // mix samples
- for(int item = 0; item < item_read; item++) {
- mixbuffer[item] += (filebuffer[item] * current->sound->gain);
- }
- previous = current;
- current = current->next;
- } else {
- // dequeue element
- PlayItem *tmp = current;
- current = current->next;
- if (tmp == play_queue)
- play_queue = current;
- else
- previous->next = current;
- fprintf (stderr, " * dequeue item %p\n", tmp);
- free_sound (tmp->sound);
- free_play_item (tmp);
- }
- }
- pthread_mutex_unlock(&mix_mutex);
- // convert from float to short
- for(int item = 0; item < buflen; item++) {
- int value = mixbuffer[item] * 32768;
- // limit to valid short values
- if (value > 32767)
- value = 32767;
- else if (value < -32768)
- value = -32768;
- outbuffer[item] = (short) value;
- }
- ao_play(device, (char *)outbuffer, buflen);
- }
- ao_close(device);
- free(filebuffer);
- free(mixbuffer);
- free(outbuffer);
- return NULL;
- }
Add Comment
Please, Sign In to add comment