diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..31b0566 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ + +all: + mkdir -p build + cmake -S ao-play-async -B build + (cd build; make) + +install: + mkdir -p ../../lib + SUBDIR=`racket -e "(display (format \"~a-~a\" (system-type 'os*) (system-type 'arch)))"`; \ + FILES=`ls build/*.so` 2>/dev/null; if [ "$$FILES" != "" ]; then cp $$FILES ../../lib/$$SUBDIR; fi + SUBDIR=`racket -e "(display (format \"~a-~a\" (system-type 'os*) (system-type 'arch)))"`; \ + FILES=`ls build/*.dll` 2>/dev/null; if [ "$$FILES" != "" ]; then cp $$FILES ../../lib/$$SUBDIR; fi + + +clean: + rm -rf build diff --git a/ao-play-async/.gitignore b/ao-play-async/.gitignore new file mode 100644 index 0000000..4a0b530 --- /dev/null +++ b/ao-play-async/.gitignore @@ -0,0 +1,74 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* +CMakeLists.txt.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/ao-play-async/CMakeLists.txt b/ao-play-async/CMakeLists.txt new file mode 100644 index 0000000..e20a189 --- /dev/null +++ b/ao-play-async/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.14) + +project(ao-play-async LANGUAGES C) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +add_library(ao-play-async SHARED + ao_playasync.c + ao_playasync.h +) + +target_compile_definitions(ao-play-async PRIVATE AOPLAYASYNC_LIBRARY) diff --git a/ao-play-async/ao_playasync.c b/ao-play-async/ao_playasync.c new file mode 100644 index 0000000..e35dae2 --- /dev/null +++ b/ao-play-async/ao_playasync.c @@ -0,0 +1,297 @@ +#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; + 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 +} + +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); +} + +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; +} + +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); +} + + diff --git a/ao-play-async/ao_playasync.h b/ao-play-async/ao_playasync.h new file mode 100644 index 0000000..4352ed9 --- /dev/null +++ b/ao-play-async/ao_playasync.h @@ -0,0 +1,26 @@ +#ifndef AO_PLAYASYNC_H +#define AO_PLAYASYNC_H + +#ifdef _WINDOWS +#ifdef AOPLAYASYNC_LIBRARY +#define AOPLAYASYNC_EXPORT __declspec(dllexport) +#else +#define AOPLAYASYNC_EXPORT __declspec(dllimport) +#endif +#else +#define AOPLAYASYNC_EXPORT extern +#endif + +AOPLAYASYNC_EXPORT void *ao_create_async(void *ao_handle, void *ao_play_f); +AOPLAYASYNC_EXPORT void ao_stop_async(void *handle); +AOPLAYASYNC_EXPORT void ao_play_async(void *handle, double at_second, double music_duration, int buf_size, void *mem); +AOPLAYASYNC_EXPORT void ao_clear_async(void *handle); + +AOPLAYASYNC_EXPORT double ao_is_at_second_async(void *handle); +AOPLAYASYNC_EXPORT double ao_music_duration_async(void *handle); + +AOPLAYASYNC_EXPORT void ao_pause_async(void *ao_handle, int paused); + +AOPLAYASYNC_EXPORT int ao_bufsize_async(void *handle); + +#endif // AO_PLAYASYNC_H