diff --git a/ao-play-async/ao_playasync.c b/ao-play-async/ao_playasync.c index 6870344..76845a5 100644 --- a/ao-play-async/ao_playasync.c +++ b/ao-play-async/ao_playasync.c @@ -1,17 +1,23 @@ #include "ao_playasync.h" #include "../ffi_version.h" -#ifdef WIN32 +#define TRUE (1==1) +#define FALSE (1==0) + +#if defined(_WIN32) || defined(WIN32) +#define AO_ASYNC_WINDOWS #include #define USE_WINDOWS_THREADS #define sleep_ms(ms) Sleep(ms) #else #ifdef __APPLE__ +#define AO_ASYNC_APPLE #define USE_DISPATCH #include #define sleep_ms(ms) msleep(ms) #else #define USE_PTHREADS +#include #include #define sleep_ms(ms) usleep(ms * 1000) #endif @@ -61,6 +67,7 @@ #define MUTEX_UNLOCK(m) pthread_mutex_unlock(&m) #define SEM_WAIT(sem, ms) _SEM_WAIT(sem, ms) #define SEM_TRYWAIT(sem) (dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW) == 0) + #define SEM_WAIT_INFINITE(sem) (dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER) == 0) #define SEM_POST(sem) dispatch_semaphore_signal(sem) #define YIELD() sleep_ms(5) #endif @@ -70,6 +77,7 @@ #define MUTEX_UNLOCK(m) ReleaseMutex(m) #define SEM_WAIT(sem, ms) (WaitForSingleObject(sem, ms) == WAIT_OBJECT_0) #define SEM_TRYWAIT(sem) (WaitForSingleObject(sem, 0) == WAIT_OBJECT_0) + #define SEM_WAIT_INFINITE(sem) (WaitForSingleObject(sem, INFINITE) == WAIT_OBJECT_0) #define SEM_POST(sem) ReleaseSemaphore(sem, 1, NULL) #define YIELD() sleep_ms(5) #endif @@ -100,24 +108,25 @@ #define MUTEX_LOCK(m) pthread_mutex_lock(&m) #define MUTEX_UNLOCK(m) pthread_mutex_unlock(&m) #define SEM_WAIT(sem, ms) _SEM_WAIT(&sem, ms) + #define SEM_WAIT_INFINITE(sem) (sem_wait(&sem) == 0) #define SEM_TRYWAIT(sem) (sem_trywait(&sem) == 0) #define SEM_POST(sem) sem_post(&sem) #define YIELD() yield() #endif -#ifndef WIN32 +#ifndef AO_ASYNC_WINDOWS #include #endif -#ifndef __APPLE__ +#ifndef AO_ASYNC_APPLE #include #endif #include #include #include #include -#include #include +#include typedef enum { PLAY = 1, @@ -143,6 +152,7 @@ typedef struct { Queue_t *last_frame; int paused; + int stopped; ao_device *ao_device; int requested_bits_per_sample; @@ -153,31 +163,41 @@ typedef struct { #ifdef USE_WINDOWS_THREADS HANDLE mutex; - HANDLE pause_mutex; HANDLE clear_mutex; HANDLE thread; DWORD thread_id; HANDLE queue_sem; + HANDLE pause_sem; #endif #if defined(USE_PTHREADS) || defined(USE_DISPATCH) pthread_mutex_t mutex; - pthread_mutex_t pause_mutex; pthread_mutex_t clear_mutex; pthread_t thread; #ifdef USE_PTHREADS sem_t queue_sem; + sem_t pause_sem; #endif #ifdef USE_DISPATCH dispatch_semaphore_t queue_sem; + dispatch_semaphore_t pause_sem; #endif #endif double at_second; double music_duration; int at_music_id; int buf_size; - int volume_in_10000; // volume in 100000 steps, i.e. 100000 equals 100% + int volume_in_10000; // volume in 10000 steps, i.e. 10000 equals 100% } AO_Handle; +static inline int stopped(AO_Handle *h, int lock_mutex) +{ + if (lock_mutex) MUTEX_LOCK(h->mutex); + int stopped = h->stopped; + if (lock_mutex) MUTEX_UNLOCK(h->mutex); + return stopped; +} + +static void del_elem(Queue_t *q); static Queue_t *get(AO_Handle *h, int ms_wait) { @@ -205,9 +225,20 @@ static Queue_t *get(AO_Handle *h, int ms_wait) return q; } -static void add(AO_Handle *h, Queue_t *elem) +static void add(AO_Handle *h, Queue_t *elem, int in_clear) { + if (!in_clear) { + MUTEX_LOCK(h->clear_mutex); + } + MUTEX_LOCK(h->mutex); + if (!in_clear && stopped(h, FALSE)) { + MUTEX_UNLOCK(h->mutex); + MUTEX_UNLOCK(h->clear_mutex); + del_elem(elem); + return; + } + if (h->last_frame == NULL) { h->play_queue = elem; elem->next = NULL; @@ -221,17 +252,34 @@ static void add(AO_Handle *h, Queue_t *elem) } h->buf_size += elem->buflen; SEM_POST(h->queue_sem); + MUTEX_UNLOCK(h->mutex); + + if (!in_clear) { + MUTEX_UNLOCK(h->clear_mutex); + } } static Queue_t *new_elem(int command, int music_id, double at_second, double music_duration, int buf_len, void *buf) { Queue_t *q = (Queue_t *) malloc(sizeof(Queue_t)); + if (q == NULL) { + fprintf(stderr, "new_elem: Cannot allocate Queue Element!\n"); + return NULL; + } + void *new_buf; - if (buf_len != 0) { + if (buf_len > 0) { new_buf = (void *) malloc(buf_len); - memcpy(new_buf, buf, buf_len); + if (new_buf != NULL) { + memcpy(new_buf, buf, buf_len); + } else { + fprintf(stderr, "new_elem: Cannot allocate memory of size %d\n", buf_len); + buf_len = 0; + free(q); + return NULL; + } } else { new_buf = NULL; } @@ -254,7 +302,7 @@ static void del_elem(Queue_t *q) free(q); } -static void clear(AO_Handle *h) +static void clear(AO_Handle *h, int do_stop) { //fprintf(stderr, "Wait for clear mutex\n"); MUTEX_LOCK(h->clear_mutex); @@ -267,6 +315,16 @@ static void clear(AO_Handle *h) q = get(h, 0); } fprintf(stderr, "%d elements cleared\n", count); + + if (do_stop) { + Queue_t *q = new_elem(STOP, 0, 0.0, 0.0, 0, NULL); + if (q == NULL) { + fprintf(stderr, "Unexpected! Cannot allocate STOP element, aborting\n"); + abort(); + } + add(h, q, TRUE); + } + MUTEX_UNLOCK(h->clear_mutex); } @@ -276,6 +334,7 @@ static int inline littleEndian() return (*(char *)&n) == 1; } +/* static void inline adjustVolume(AO_Handle *handle, char *_buf, int buf_size, int volume_in_10000) { int bytes_per_sample = (handle->dev_bits_per_sample >> 3); @@ -318,6 +377,59 @@ static void inline adjustVolume(AO_Handle *handle, char *_buf, int buf_size, int } } } +*/ + + +static inline int32_t read_sample(unsigned char *mem, int req_bytes, int little_endian) +{ + uint32_t v = 0; + + for (int i = 0; i < req_bytes; i++) { + int idx = little_endian ? i : (req_bytes - i - 1); + v |= ((uint32_t) mem[idx]) << (8 * i); + } + + if (req_bytes < 4) { + int bits = req_bytes * 8; + uint32_t sign = 1u << (bits - 1); + v = (v ^ sign) - sign; + } + + return (int32_t) v; +} + +static inline void store_sample(unsigned char *dst, int32_t in_sample, int out_bytes, int is_little_endian) +{ + uint32_t sample = (uint32_t) in_sample; + for (int i = 0; i < out_bytes; i++) { + int idx = is_little_endian ? i : (out_bytes - i - 1); + dst[idx] = sample & 0xff; + sample >>= 8; + } +} + + +static inline void adjustVolume(AO_Handle *handle, + char *_buf, + int buf_size, + int volume_in_10000) +{ + int bytes_per_sample = handle->dev_bits_per_sample >> 3; + int endianess = handle->dev_endianess; + int little_endian = (endianess == AO_FMT_LITTLE); + + if (!little_endian && endianess == AO_FMT_NATIVE) { + little_endian = littleEndian(); + } + + unsigned char *buf = (unsigned char *) _buf; + + for (int i = 0; i + bytes_per_sample <= buf_size; i += bytes_per_sample) { + int32_t sample = read_sample(buf + i, bytes_per_sample, little_endian); + int64_t scaled = ((int64_t) sample * volume_in_10000) / 10000; + store_sample(buf + i, (int32_t) scaled, bytes_per_sample, little_endian); + } +} #if defined(USE_PTHREADS) || defined(USE_DISPATCH) static void *run(void *arg) @@ -330,25 +442,36 @@ static DWORD run(LPVOID arg) int go_on = (1 == 1); while(go_on) { - MUTEX_LOCK(handle->pause_mutex); - MUTEX_UNLOCK(handle->pause_mutex); + SEM_WAIT_INFINITE(handle->pause_sem); + SEM_POST(handle->pause_sem); MUTEX_LOCK(handle->clear_mutex); Queue_t *q = get(handle, 250); MUTEX_UNLOCK(handle->clear_mutex); if (q != NULL) { + + MUTEX_LOCK(handle->mutex); handle->at_second = q->at_second; handle->music_duration = q->music_duration; handle->at_music_id = q->music_id; + int volume = handle->volume_in_10000; + MUTEX_UNLOCK(handle->mutex); - if (handle->volume_in_10000 != 10000) { + if (volume != 10000 && q->command == PLAY) { // adjust volume - adjustVolume(handle, q->buf, q->buflen, handle->volume_in_10000); + adjustVolume(handle, q->buf, q->buflen, volume); } if (q->command == STOP) { - go_on = (1 == 0); + go_on = FALSE; } else { - ao_play(handle->ao_device, (char *) q->buf, q->buflen); + if (!ao_play(handle->ao_device, (char *) q->buf, q->buflen)) { + fprintf(stderr, "Unexpected, ao_play returns 0 --> ao device must be closed\n"); + fprintf(stderr, "Stopping play loop\n"); + go_on = FALSE; + MUTEX_LOCK(handle->mutex); + handle->stopped = TRUE; + MUTEX_UNLOCK(handle->mutex); + } } del_elem(q); @@ -370,12 +493,12 @@ int ao_async_version() return ffi_version(); } -#ifdef _WIN32 +#ifdef AO_ASYNC_WINDOWS #else #include #endif -#ifdef _WIN32 +#ifdef AO_ASYNC_WINDOWS static void at_exit_shutdown_ao(void) #else static void at_exit_shutdown_ao() @@ -434,6 +557,7 @@ static ao_device *try_open_device(int bits, int rate, int channels, int byte_for } +/* static inline int32_t convert_bits(int32_t sample, int req_bits, int out_bits) { if (req_bits > out_bits) { @@ -443,41 +567,36 @@ static inline int32_t convert_bits(int32_t sample, int req_bits, int out_bits) } return sample; } +*/ -static inline int32_t read_sample(unsigned char *mem, int req_bytes, int little_endian) +static inline int32_t convert_bits(int32_t sample, int req_bits, int out_bits) { - uint32_t v = 0; + if (req_bits == out_bits) return sample; - for (int i = 0; i < req_bytes; i++) { - int idx = little_endian ? i : (req_bytes - i - 1); - v |= ((uint32_t) mem[idx]) << (8 * i); - } + int shift = req_bits > out_bits + ? req_bits - out_bits + : out_bits - req_bits; - if (req_bytes < 4) { - int bits = req_bytes * 8; - uint32_t sign = 1u << (bits - 1); - v = (v ^ sign) - sign; - } - - return (int32_t) v; + if (req_bits > out_bits) { + return sample / (1 << shift); + } else { + return (int32_t)((int64_t)sample * ((int64_t)1 << shift)); + } } -static inline void store_sample(unsigned char *dst, int32_t sample, int out_bytes, int is_little_endian) -{ - for (int i = 0; i < out_bytes; i++) { - int idx = is_little_endian ? i : (out_bytes - i - 1); - dst[idx] = sample & 0xff; - sample >>= 8; - } -} static void *convert_req_to_real(AO_Handle *h, void *mem, int mem_size, BufferInfo_t *info, int *out_size) { + if (mem_size <= 0) { + *out_size = 0; + return NULL; + } + int endianess = h->dev_endianess; int little_endian = (endianess == AO_FMT_LITTLE); if (!little_endian && endianess == AO_FMT_NATIVE) little_endian = littleEndian(); - int requested_bits = h->requested_bits_per_sample; + int requested_bits = info->sample_bits; // h->requested_bits_per_sample; int output_bits = h->dev_bits_per_sample; int req_bytes = (requested_bits / 8); @@ -485,7 +604,7 @@ static void *convert_req_to_real(AO_Handle *h, void *mem, int mem_size, BufferIn int samples = (mem_size / req_bytes); - unsigned char *buf_out = (unsigned char *) malloc(samples * out_bytes); + unsigned char *buf_out = (unsigned char *) malloc((samples + 1) * out_bytes); if (buf_out == NULL) { fprintf(stderr, "Allocation of output buffer of %d samples of %d bits gives NULL", samples, output_bits); *out_size = 0; @@ -520,13 +639,10 @@ void *ao_create_async(int bits, int rate, int channels, int byte_format, const c init_ao(); AO_Handle *handle = (AO_Handle *) malloc(sizeof(AO_Handle)); - - ao_sample_format fmt; - fmt.bits = bits; - fmt.rate = rate; - fmt.byte_format = byte_format; - fmt.channels = channels; - fmt.matrix = NULL; + if (handle == NULL) { + fprintf(stderr, "Cannot allocate ao_handle!\n"); + return NULL; + } int opened_bits = 0; ao_device *dev = try_open_device(bits, rate, channels, byte_format, wav_file_output, &opened_bits); @@ -549,23 +665,23 @@ void *ao_create_async(int bits, int rate, int channels, int byte_format, const c handle->last_frame = NULL; handle->at_second = -1; handle->at_music_id = -1; + handle->music_duration = 0; handle->buf_size = 0; - handle->paused = (1 == 0); + handle->paused = FALSE; + handle->stopped = FALSE; #if defined(USE_PTHREADS) || defined(USE_DISPATCH) - pthread_mutex_t p = PTHREAD_MUTEX_INITIALIZER; - handle->pause_mutex = p; - pthread_mutex_t c = PTHREAD_MUTEX_INITIALIZER; - handle->clear_mutex = c; - pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; - handle->mutex = m; + pthread_mutex_init(&handle->clear_mutex, NULL); + pthread_mutex_init(&handle->mutex, NULL); #ifdef USE_PTHREADS sem_init(&handle->queue_sem, 0, 0); + sem_init(&handle->pause_sem, 0, 1); #endif #ifdef USE_DISPATCH handle->queue_sem = dispatch_semaphore_create(0); + handle->pause_sem = dispatch_semaphore_create(1); #endif pthread_create(&handle->thread, NULL, run, handle); #endif @@ -575,19 +691,14 @@ void *ao_create_async(int bits, int rate, int channels, int byte_format, const c handle->mutex = CreateMutex(NULL, // default security attributes FALSE, // initially not owned NULL); - handle->pause_mutex = CreateMutex(NULL, // default security attributes - FALSE, // initially not owned - NULL); handle->clear_mutex = CreateMutex(NULL, FALSE, NULL); handle->queue_sem = CreateSemaphore(NULL, 0, 1000000, NULL); + handle->pause_sem = CreateSemaphore(NULL, 1, 1000000, NULL); handle->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) run, handle, 0, &handle->thread_id); #endif - MUTEX_UNLOCK(handle->pause_mutex); - MUTEX_UNLOCK(handle->clear_mutex); - return (void *) handle; } @@ -596,15 +707,19 @@ void ao_stop_async(void *ao_handle) AO_Handle *h = (AO_Handle *) ao_handle; fprintf(stderr, "stopping ao_async, calling clear\n"); - clear(h); + MUTEX_LOCK(h->mutex); + h->stopped = TRUE; + MUTEX_UNLOCK(h->mutex); + + clear(h, TRUE); fprintf(stderr, "queue cleared\n"); + MUTEX_LOCK(h->mutex); if (h->paused) { - MUTEX_UNLOCK(h->pause_mutex); + SEM_POST(h->pause_sem); } - Queue_t *q = new_elem(STOP, 0, 0.0, 0.0, 0, NULL); - add(h, q); + MUTEX_UNLOCK(h->mutex); fprintf(stderr, "stop command queued\n"); @@ -614,9 +729,14 @@ void ao_stop_async(void *ao_handle) #ifdef USE_PTHREADS sem_destroy(&h->queue_sem); + sem_destroy(&h->pause_sem); + #endif + + #ifdef USE_DISPATCH + dispatch_release(h->queue_sem); + dispatch_release(h->pause_sem); #endif - pthread_mutex_destroy(&h->pause_mutex); pthread_mutex_destroy(&h->clear_mutex); pthread_mutex_destroy(&h->mutex); #endif @@ -625,6 +745,9 @@ void ao_stop_async(void *ao_handle) WaitForSingleObject(h->thread, INFINITE); CloseHandle(h->thread); CloseHandle(h->mutex); + CloseHandle(h->pause_sem); + CloseHandle(h->queue_sem); + CloseHandle(h->clear_mutex); #endif ao_close(h->ao_device); @@ -635,10 +758,7 @@ void ao_stop_async(void *ao_handle) fprintf(stderr, "async handle freed\n"); } -#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 big_endian, unsigned char b[4]) { int i; @@ -657,27 +777,38 @@ static inline void make_sample_bytes(int32_t sample, int bytes_per_sample, int b } } } +*/ + void ao_play_async(void *ao_handle, int music_id, double at_second, double music_duration, int buf_size, void *mem, BufferInfo_t info) { AO_Handle *h = (AO_Handle *) ao_handle; + if (stopped(h, TRUE)) { return; } + Queue_t *q = NULL; int ao_size = 0; void *ao_mem = convert_req_to_real(ao_handle, mem, buf_size, &info, &ao_size); + if (ao_mem == NULL || ao_size <= 0) { + if (ao_mem != NULL) { free(ao_mem); } + return; + } q = new_elem(PLAY, music_id, at_second, music_duration, ao_size, ao_mem); free(ao_mem); - add(h, q); + if (q != NULL && q->buf != NULL) { // memory error has already been given. + add(h, q, FALSE); + } } void ao_clear_async(void *ao_handle) { AO_Handle *h = (AO_Handle *) ao_handle; - clear(h); + if (stopped(h, TRUE)) { return; } + clear(h, FALSE); } double ao_is_at_second_async(void *ao_handle) @@ -746,12 +877,12 @@ void ao_pause_async(void *ao_handle, int paused) if (h->paused) { if (!paused) { h->paused = paused; - MUTEX_UNLOCK(h->pause_mutex); + SEM_POST(h->pause_sem); } } else { if (paused) { h->paused = paused; - MUTEX_LOCK(h->pause_mutex); + SEM_WAIT_INFINITE(h->pause_sem); } } diff --git a/lib/windows-x86_64.zip b/lib/windows-x86_64.zip index 0c66e90..48f7575 100644 Binary files a/lib/windows-x86_64.zip and b/lib/windows-x86_64.zip differ diff --git a/lib/windows-x86_64/ao-play-async.dll b/lib/windows-x86_64/ao-play-async.dll index c5c7d7f..89c9756 100644 Binary files a/lib/windows-x86_64/ao-play-async.dll and b/lib/windows-x86_64/ao-play-async.dll differ diff --git a/lib/windows-x86_64/ffmpeg_audio.dll b/lib/windows-x86_64/ffmpeg_audio.dll index 46db0df..c6e1268 100644 Binary files a/lib/windows-x86_64/ffmpeg_audio.dll and b/lib/windows-x86_64/ffmpeg_audio.dll differ diff --git a/lib/windows-x86_64/ffmpeg_audio.lib b/lib/windows-x86_64/ffmpeg_audio.lib index 2144e55..cf30f34 100644 Binary files a/lib/windows-x86_64/ffmpeg_audio.lib and b/lib/windows-x86_64/ffmpeg_audio.lib differ