#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 #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; int paused; } 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->paused) && (handle->play_queue != NULL); if (has_frames) { Queue_t *q = get(handle); handle->at_second = q->at_second; handle->music_duration = q->music_duration; 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 } int ao_async_version() { return VERSION; } 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; handle->paused = (1 == 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); } /* (define (abs x) (if (>= x 0) x (* x -1))) (define (make-sample-bytes sample bytes-per-sample endianess) (letrec ((mk (lambda (i d) (if (< i bytes-per-sample) (cons (bitwise-and d 255) (mk (+ i 1) (arithmetic-shift d -8))) '())))) (let ((bytes (mk 0 sample))) (if (eq? endianess 'big-endian) (reverse bytes) bytes)))) ;(get-sample (lambda (k channel) ; (let ((chan-buf (list-ref buffer channel))) ; (vector-ref chan-buf k)))) ) ;(letrec ((i 0) ; (fill (lambda (k channel) ; (if (< k buf-len) ; (if (< channel channels) ; (let* ((sample (get-sample k channel)) ; (bytes (make-sample-bytes sample bytes-per-sample endianess)) ; ) ; (for-each (lambda (byte) ; (ptr-set! audio _byte i byte) ; (set! i (+ i 1))) ; bytes) ; ;; process sample ; (fill k (+ channel 1))) ; (fill (+ k 1) 0)) ; 'filled)) ; )) ; (fill 0 0) */ #define AO_FMT_LITTLE 1 #define AO_FMT_BIG 2 #define AO_FMT_NATIVE 4 static inline void make_sample_bytes(int32_t sample, int bytes_per_sample, int endianess, unsigned char b[4]) { int i; for (i = 0; i < bytes_per_sample; i++) { b[i] = sample&0xff; sample = sample >> 8; } if (endianess == AO_FMT_BIG) { unsigned char b1[4] = { 0, 0, 0, 0 }; for(i = 0; i < bytes_per_sample; i++) { b1[bytes_per_sample - i - 1] = b[i]; } for(i = 0; i < bytes_per_sample; i++) { b[i] = b1[i]; } } } void *convertFlac(void *mem, int buf_len, BufferInfo_t *info, int *audio_size) { // buf_size equals number of samples of 32bit for all channels. So buf_size for flac = 4 * buf_len * channels int bytes = info->sample_bits / 8; int endianness = info->endiannes; int store_size = info->channels * bytes * buf_len; unsigned char *new_mem = (unsigned char *) malloc(store_size); *audio_size = store_size; int32_t **buffer = (int32_t **) mem; int i, k, channel; i = 0; for(k = 0; k < buf_len; k++) { for(channel = 0; channel < info->channels; channel++) { int32_t *chan = buffer[channel]; int32_t sample = chan[k]; unsigned char b[4]; make_sample_bytes(sample, bytes, endianness, b); for(int j = 0; j < bytes; j++) { new_mem[i++] = b[j]; } } } return (void *) new_mem; } void ao_play_async(void *ao_handle, double at_second, double music_duration, int buf_size, void *mem, BufferInfo_t info) { AO_Handle *h = (AO_Handle *) ao_handle; Queue_t *q = NULL; switch(info.type) { case flac: { int store_size = 0; void *store_mem = convertFlac(mem, buf_size, &info, &store_size); q = new_elem(PLAY, at_second, music_duration, store_size, store_mem); free(store_mem); } break; case ao: { q = new_elem(PLAY, at_second, music_duration, buf_size, mem); } break; case mpg123: { static int warned = 0; if (!warned) { warned = 1; fprintf(stderr, "format mpg123 not supported yet\n"); } return; } break; case ao_ogg: { static int warned = 0; if (!warned) { warned = 1; fprintf(stderr, "format ao_ogg not supported yet\n"); } return; } break; } 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; } void ao_pause_async(void *ao_handle, int paused) { AO_Handle *h = (AO_Handle *) ao_handle; MUTEX_LOCK(h->mutex); h->paused = paused; MUTEX_UNLOCK(h->mutex); }