#include "ao_playasync.h" #ifdef WIN32 #include #define USE_WINDOWS_THREADS #define sleep_ms(ms) Sleep(ms) #else #define USE_PTHREADS #define sleep_ms(ms) usleep(ms * 1000) #endif #ifdef USE_WINDOWS_THREADS #define MUTEX_LOCK(m) WaitForSingleObject(m, INFINITE) #define MUTEX_UNLOCK(m) ReleaseMutex(m) #endif #ifdef USE_PTHREADS #include #define MUTEX_LOCK(m) pthread_mutex_lock(&m) #define MUTEX_UNLOCK(m) pthread_mutex_unlock(&m) #endif #ifndef WIN32 #include #endif #include #include #include #include #include #include typedef enum { PLAY = 1, STOP = 2 } Command_t; typedef void * ao_device; typedef struct _queue_ { Command_t command; void *buf; int buflen; double at_second; double music_duration; struct _queue_ *next; struct _queue_ *prev; } Queue_t; typedef int(*ao_play_func_t)(void *, char *, uint32_t); typedef struct { Queue_t *play_queue; Queue_t *last_frame; ao_device *ao_device; #ifdef USE_WINDOWS_THREADS HANDLE mutex; HANDLE thread; DWORD thread_id; #endif #ifdef USE_PTHREADS pthread_mutex_t mutex; pthread_t thread; #endif double at_second; double music_duration; ao_play_func_t ao_play_f; int buf_size; } AO_Handle; //static int(*ao_play)(void *device, char *samples, uint32_t n) = NULL; static Queue_t *front(AO_Handle *h) { assert(h->play_queue != NULL); return h->play_queue; } static Queue_t *get(AO_Handle *h) { assert(h->play_queue != NULL); Queue_t *q = h->play_queue; h->play_queue = h->play_queue->next; if (h->play_queue == NULL) { h->last_frame = NULL; } else { h->play_queue->prev = NULL; } h->buf_size -= q->buflen; return q; } static void add(AO_Handle *h, Queue_t *elem) { if (h->last_frame == NULL) { h->play_queue = elem; elem->next = NULL; elem->prev = NULL; h->last_frame = h->play_queue; } else { h->last_frame->next = elem; elem->prev = h->last_frame; elem->next = NULL; h->last_frame = elem; } h->buf_size += elem->buflen; } static Queue_t *new_elem(int command, double at_second, double music_duration, int buf_len, void *buf) { Queue_t *q = (Queue_t *) malloc(sizeof(Queue_t)); void *new_buf; if (buf_len != 0) { new_buf = (void *) malloc(buf_len); memcpy(new_buf, buf, buf_len); } else { new_buf = NULL; } q->at_second = at_second; q->music_duration = music_duration; q->buf = new_buf; q->buflen = buf_len; q->command = command; q->next = NULL; q->prev = NULL; return q; } static void del_elem(Queue_t *q) { if (q->buflen != 0) { free(q->buf); } free(q); } static void clear(AO_Handle *h) { while (h->play_queue != NULL) { Queue_t *q = get(h); del_elem(q); } } #ifdef USE_PTHREADS static void *run(void *arg) #endif #ifdef USE_WINDOWS_THREADS static DWORD run(LPVOID arg) #endif { AO_Handle *handle = (AO_Handle *) arg; int go_on = (1 == 1); while(go_on) { MUTEX_LOCK(handle->mutex); int has_frames = (handle->play_queue != NULL); if (has_frames) { Queue_t *q = get(handle); handle->at_second = q->at_second; MUTEX_UNLOCK(handle->mutex); if (q->command == STOP) { go_on = (1 == 0); } else { //fprintf(stderr, "playing buf at %lf\n", handle->at_second); handle->ao_play_f(handle->ao_device, q->buf, q->buflen); } del_elem(q); } else { MUTEX_UNLOCK(handle->mutex); sleep_ms(5); // sleep for 5ms } } #ifdef USE_PTHREADS return NULL; #endif #ifdef USE_WINDOWS_THREADS return 0; #endif } void *ao_create_async(void *ao_device_yeah, void *ao_play_f) { //if (ao_play == NULL) { get_ao_play(); } AO_Handle *handle = (AO_Handle *) malloc(sizeof(AO_Handle)); handle->ao_device = (ao_device *) ao_device_yeah; handle->play_queue = NULL; handle->last_frame = NULL; handle->at_second = -1; handle->ao_play_f = ao_play_f; handle->buf_size = 0; #ifdef USE_PTHREADS pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; handle->mutex = m; pthread_create(&handle->thread, NULL, run, handle); #endif #ifdef USE_WINDOWS_THREADS handle->mutex = CreateMutex(NULL, // default security attributes FALSE, // initially not owned NULL); handle->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) run, handle, 0, &handle->thread_id); #endif return (void *) handle; } void ao_stop_async(void *ao_handle) { AO_Handle *h = (AO_Handle *) ao_handle; MUTEX_LOCK(h->mutex); clear(h); Queue_t *q = new_elem(STOP, 0.0, 0.0, 0, NULL); add(h, q); MUTEX_UNLOCK(h->mutex); #ifdef USE_PTHREADS void *retval; pthread_join(h->thread, &retval); #endif #ifdef USE_WINDOWS_THREADS WaitForSingleObject(h->thread, INFINITE); CloseHandle(h->thread); CloseHandle(h->mutex); #endif free(h); } void ao_play_async(void *ao_handle, double at_second, double music_duration, int buf_size, void *mem) { AO_Handle *h = (AO_Handle *) ao_handle; Queue_t *q = new_elem(PLAY, at_second, music_duration, buf_size, mem); MUTEX_LOCK(h->mutex); add(h, q); MUTEX_UNLOCK(h->mutex); } void ao_clear_async(void *ao_handle) { AO_Handle *h = (AO_Handle *) ao_handle; MUTEX_LOCK(h->mutex); clear(h); MUTEX_UNLOCK(h->mutex); } double ao_is_at_second_async(void *ao_handle) { AO_Handle *h = (AO_Handle *) ao_handle; MUTEX_LOCK(h->mutex); double s = h->at_second; MUTEX_UNLOCK(h->mutex); return s; } double ao_music_duration_async(void *ao_handle) { AO_Handle *h = (AO_Handle *) ao_handle; MUTEX_LOCK(h->mutex); double duration = h->music_duration; MUTEX_UNLOCK(h->mutex); return duration; } int ao_bufsize_async(void *ao_handle) { AO_Handle *h = (AO_Handle *) ao_handle; MUTEX_LOCK(h->mutex); int s = h->buf_size; MUTEX_UNLOCK(h->mutex); return s; }