12 Commits

Author SHA1 Message Date
hans a9151fdb86 music id added 2026-04-15 13:46:27 +02:00
hans 298cde0779 moved ao library access to ao-play-async, because it only provides overhead and no benefits. All playing will be done via ao-play-async 2026-04-15 09:39:59 +02:00
hans 373da4aa18 using semaphores... 2026-04-14 18:11:34 +02:00
hans d33bdb3482 created semaphore for queue and mutex for pause 2026-04-14 15:08:32 +02:00
hans d5957c86bd zlib dependency added for windows 2026-04-11 22:12:15 +02:00
hans 6bcb2702a9 removed fprintf stderrs 2026-04-10 08:40:18 +02:00
hans be4f0ff9dd - 2026-04-10 08:32:06 +02:00
hans 09955bebdc add dlls 2026-04-07 14:14:00 +02:00
hans 2879e3f606 - 2026-04-07 14:12:58 +02:00
hans 424128975d - 2026-04-07 14:12:08 +02:00
hans a3119a2444 taglib on windows. 2026-04-07 14:09:13 +02:00
hans 18803c36f1 - 2026-04-07 13:45:54 +02:00
17 changed files with 397 additions and 78 deletions
-15
View File
@@ -12,23 +12,8 @@
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
+14 -4
View File
@@ -1,16 +1,26 @@
all:
mkdir -p build
cmake -S ao-play-async -B build
(cd build; make)
install:
mkdir -p ../../lib
install: all
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
mkdir -p lib/$$SUBDIR
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
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
test: install
cp lib/linux-x86_64/*.so ~/.local/share/racket/racket-sound-lib/linux-x86_64
zip: install
SUBDIR=`racket -e "(display (format \"~a-~a\" (system-type 'os*) (system-type 'arch)))"`; \
(cd lib; zip -y -r -9 $$SUBDIR.zip $$SUBDIR)
SUBDIR=`racket -e "(display (format \"~a-~a\" (system-type 'os*) (system-type 'arch)))"`; \
(cd lib; rm -rf $$SUBDIR)
clean:
rm -rf build
+1
View File
@@ -72,3 +72,4 @@ CMakeLists.txt.user*
*.dll
*.exe
build
+2
View File
@@ -10,4 +10,6 @@ add_library(ao-play-async SHARED
ao_playasync.h
)
target_link_libraries(ao-play-async PRIVATE ao)
target_compile_definitions(ao-play-async PRIVATE AOPLAYASYNC_LIBRARY)
+313 -43
View File
@@ -16,27 +16,74 @@
#ifdef USE_PTHREADS
#include <pthread.h>
#include <semaphore.h>
#include <sched.h>
#define TIME_NS_IN_MSEC 1000000ULL
static void makeSemTimeoutTime(struct timespec *ts, int ms) {
clock_gettime(CLOCK_REALTIME, ts);
ts->tv_sec += ms / 1000;
ts->tv_nsec += (ms % 1000) * TIME_NS_IN_MSEC;
if (ts->tv_nsec >= 1000000000L) {
ts->tv_sec++;
ts->tv_nsec = ts->tv_nsec - 1000000000L;
}
}
static int _SEM_WAIT(sem_t *sem, int ms)
{
struct timespec ts;
makeSemTimeoutTime(&ts, ms);
int r = sem_timedwait(sem, &ts);
return (r == 0);
}
static int msleep(long msec)
{
struct timespec ts;
int res;
if (msec < 0) {
return -1;
}
ts.tv_sec = msec / 1000;
ts.tv_nsec = (msec % 1000) * 1000000;
res = nanosleep(&ts, &ts);
return res;
}
static void yield()
{
msleep(5);
}
#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_TRYWAIT(sem) (sem_trywait(&sem) == 0)
#define SEM_POST(sem) sem_post(&sem)
#define YIELD() yield()
#endif
#ifndef WIN32
#include <unistd.h>
#endif
#include <assert.h>
#include <malloc.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <ao/ao.h>
typedef enum {
PLAY = 1,
STOP = 2
} Command_t;
typedef void * ao_device;
typedef struct _queue_ {
Command_t command;
@@ -44,6 +91,7 @@ typedef struct _queue_ {
int buflen;
double at_second;
double music_duration;
int music_id;
struct _queue_ *next;
struct _queue_ *prev;
} Queue_t;
@@ -53,36 +101,43 @@ typedef int(*ao_play_func_t)(void *, char *, uint32_t);
typedef struct {
Queue_t *play_queue;
Queue_t *last_frame;
int paused;
ao_device *ao_device;
#ifdef USE_WINDOWS_THREADS
HANDLE mutex;
HANDLE pause_mutex;
HANDLE thread;
DWORD thread_id;
#endif
#ifdef USE_PTHREADS
pthread_mutex_t mutex;
pthread_mutex_t pause_mutex;
pthread_mutex_t clear_mutex;
pthread_t thread;
sem_t queue_sem;
#endif
double at_second;
double music_duration;
ao_play_func_t ao_play_f;
int at_music_id;
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)
static Queue_t *get(AO_Handle *h, int ms_wait)
{
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;
Queue_t *q = NULL;
int r;
if (ms_wait <= 0) {
r = SEM_TRYWAIT(h->queue_sem);
} else {
r = SEM_WAIT(h->queue_sem, ms_wait);
}
if (r) {
MUTEX_LOCK(h->mutex);
if (h->play_queue != NULL) { // Clear could have cleared the play_queue.
q = h->play_queue;
h->play_queue = h->play_queue->next;
if (h->play_queue == NULL) {
h->last_frame = NULL;
@@ -90,11 +145,15 @@ static Queue_t *get(AO_Handle *h)
h->play_queue->prev = NULL;
}
h->buf_size -= q->buflen;
}
MUTEX_UNLOCK(h->mutex);
}
return q;
}
static void add(AO_Handle *h, Queue_t *elem)
{
MUTEX_LOCK(h->mutex);
if (h->last_frame == NULL) {
h->play_queue = elem;
elem->next = NULL;
@@ -107,9 +166,11 @@ static void add(AO_Handle *h, Queue_t *elem)
h->last_frame = elem;
}
h->buf_size += elem->buflen;
SEM_POST(h->queue_sem);
MUTEX_UNLOCK(h->mutex);
}
static Queue_t *new_elem(int command, double at_second, double music_duration, int buf_len, void *buf)
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));
void *new_buf;
@@ -120,11 +181,12 @@ static Queue_t *new_elem(int command, double at_second, double music_duration, i
} else {
new_buf = NULL;
}
q->music_id = music_id;
q->at_second = at_second;
q->music_duration = music_duration;
q->buf = new_buf;
q->buflen = buf_len;
q->command = command;
q->command = (Command_t) command;
q->next = NULL;
q->prev = NULL;
return q;
@@ -140,10 +202,18 @@ static void del_elem(Queue_t *q)
static void clear(AO_Handle *h)
{
while (h->play_queue != NULL) {
Queue_t *q = get(h);
//fprintf(stderr, "Wait for clear mutex\n");
MUTEX_LOCK(h->clear_mutex);
//fprintf(stderr, "Starting clear\n");
int count = 0;
Queue_t *q = get(h, 0);
while (q != NULL) {
del_elem(q);
count += 1;
q = get(h, 0);
}
fprintf(stderr, "%d elements cleared\n", count);
MUTEX_UNLOCK(h->clear_mutex);
}
#ifdef USE_PTHREADS
@@ -154,30 +224,28 @@ 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);
MUTEX_LOCK(handle->pause_mutex);
MUTEX_UNLOCK(handle->pause_mutex);
MUTEX_LOCK(handle->clear_mutex);
Queue_t *q = get(handle, 250);
MUTEX_UNLOCK(handle->clear_mutex);
if (q != NULL) {
handle->at_second = q->at_second;
handle->music_duration = q->music_duration;
MUTEX_UNLOCK(handle->mutex);
handle->at_music_id = q->music_id;
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);
ao_play(handle->ao_device, (char *) q->buf, q->buflen);
}
del_elem(q);
} else {
MUTEX_UNLOCK(handle->mutex);
sleep_ms(5); // sleep for 5ms
YIELD();
}
}
@@ -189,24 +257,67 @@ static DWORD run(LPVOID arg)
#endif
}
void *ao_create_async(void *ao_device_yeah, void *ao_play_f)
int ao_async_version()
{
//if (ao_play == NULL) { get_ao_play(); }
return VERSION;
}
#ifdef _WIN32
#else
#include <dlfcn.h>
#endif
static void at_exit_shutdown_ao()
{
ao_shutdown();
}
static void init_ao()
{
static int init = 0;
if (!init) {
ao_initialize();
atexit(at_exit_shutdown_ao);
}
}
void *ao_create_async(int bits, int rate, int channels, int byte_format)
{
init_ao();
AO_Handle *handle = (AO_Handle *) malloc(sizeof(AO_Handle));
handle->ao_device = (ao_device *) ao_device_yeah;
ao_sample_format fmt;
fmt.bits = bits;
fmt.rate = rate;
fmt.byte_format = byte_format;
fmt.channels = channels;
fmt.matrix = NULL;
ao_device *dev = ao_open_live(ao_default_driver_id(), &fmt, NULL);
if (dev == NULL) {
fprintf(stderr, "Cannot open ao-device, error code = %d\n", errno);
return NULL;
}
handle->ao_device = dev;
handle->play_queue = NULL;
handle->last_frame = NULL;
handle->at_second = -1;
handle->at_music_id = -1;
handle->ao_play_f = ao_play_f;
handle->buf_size = 0;
handle->paused = (1 == 0);
#ifdef USE_PTHREADS
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;
sem_init(&handle->queue_sem, 0, 0);
pthread_create(&handle->thread, NULL, run, handle);
#endif
@@ -214,21 +325,35 @@ void *ao_create_async(void *ao_device_yeah, void *ao_play_f)
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->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) run, handle, 0, &handle->thread_id);
// TODO Windows Semaphores!
#endif
MUTEX_UNLOCK(handle->pause_mutex);
MUTEX_UNLOCK(handle->clear_mutex);
return (void *) handle;
}
void ao_stop_async(void *ao_handle)
{
AO_Handle *h = (AO_Handle *) ao_handle;
fprintf(stderr, "stopping ao_async, calling clear\n");
MUTEX_LOCK(h->mutex);
clear(h);
Queue_t *q = new_elem(STOP, 0.0, 0.0, 0, NULL);
fprintf(stderr, "queue cleared\n");
if (h->paused) {
MUTEX_UNLOCK(h->pause_mutex);
}
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");
#ifdef USE_PTHREADS
void *retval;
@@ -239,24 +364,148 @@ void ao_stop_async(void *ao_handle)
CloseHandle(h->thread);
CloseHandle(h->mutex);
#endif
ao_close(h->ao_device);
fprintf(stderr, "device closed\n");
free(h);
fprintf(stderr, "async handle freed\n");
}
void ao_play_async(void *ao_handle, double at_second, double music_duration, int buf_size, void *mem)
/*
(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, int music_id, double at_second, double music_duration, int buf_size, void *mem, BufferInfo_t info)
{
AO_Handle *h = (AO_Handle *) ao_handle;
Queue_t *q = new_elem(PLAY, at_second, music_duration, buf_size, mem);
MUTEX_LOCK(h->mutex);
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, music_id, at_second, music_duration, store_size, store_mem);
free(store_mem);
}
break;
case ao: {
q = new_elem(PLAY, music_id, 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;
}
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)
@@ -268,6 +517,15 @@ double ao_is_at_second_async(void *ao_handle)
return s;
}
int ao_is_at_music_id_async(void *ao_handle)
{
AO_Handle *h = (AO_Handle *) ao_handle;
MUTEX_LOCK(h->mutex);
int s = h->at_music_id;
MUTEX_UNLOCK(h->mutex);
return s;
}
double ao_music_duration_async(void *ao_handle)
{
AO_Handle *h = (AO_Handle *) ao_handle;
@@ -290,7 +548,19 @@ void ao_pause_async(void *ao_handle, int paused)
{
AO_Handle *h = (AO_Handle *) ao_handle;
MUTEX_LOCK(h->mutex);
if (h->paused) {
if (!paused) {
h->paused = paused;
MUTEX_UNLOCK(h->pause_mutex);
}
} else {
if (paused) {
h->paused = paused;
MUTEX_LOCK(h->pause_mutex);
}
}
MUTEX_UNLOCK(h->mutex);
}
+21 -2
View File
@@ -11,12 +11,31 @@
#define AOPLAYASYNC_EXPORT extern
#endif
AOPLAYASYNC_EXPORT void *ao_create_async(void *ao_handle, void *ao_play_f);
#define VERSION 2
typedef enum {
ao = 1,
flac = 2,
mpg123 = 3,
ao_ogg = 4
} BufferType_t;
typedef struct {
BufferType_t type;
int sample_bits;
int sample_rate;
int channels;
int endiannes;
} BufferInfo_t;
AOPLAYASYNC_EXPORT int ao_async_version(void);
AOPLAYASYNC_EXPORT void *ao_create_async(int bits, int rate, int channels, int byte_format);
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_play_async(void *handle, int music_id, double at_second, double music_duration, int buf_size, void *mem, BufferInfo_t info);
AOPLAYASYNC_EXPORT void ao_clear_async(void *handle);
AOPLAYASYNC_EXPORT double ao_is_at_second_async(void *handle);
AOPLAYASYNC_EXPORT int ao_is_at_music_id_async(void *handle);
AOPLAYASYNC_EXPORT double ao_music_duration_async(void *handle);
AOPLAYASYNC_EXPORT void ao_pause_async(void *ao_handle, int paused);
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+17
View File
@@ -0,0 +1,17 @@
Building for Win32.
Extract the taglib sources.
Add this in CMakeLists.txt:
set(ZLIB_LIBRARY "c:/devel/libraries/nwin64/lib/zlib.lib")
set(ZLIB_INCLUDE_DIR "c:/devel/libraries/nwin64/include")
set(BUILD_TESTING OFF)
set(BUILD_SHARED_LIBS ON)
Or any place you have zlib.lib stored.
Copy the shared libraries to ../lib/windows_<arch>/
+15
View File
@@ -0,0 +1,15 @@
TAGLIB=taglib-2.2.1
all:
tar xf ${TAGLIB}.tar.gz
SUBDIR=`racket -e "(display (format \"~a-~a\" (system-type 'os*) (system-type 'arch)))"`; \
(cd ${TAGLIB}; cmake -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=../lib/$$SUBDIR -DCMAKE_BUILD_TYPE=Release .)
(cd ${TAGLIB}; make)
(cd ${TAGLIB}; make install)
SUBDIR=`racket -e "(display (format \"~a-~a\" (system-type 'os*) (system-type 'arch)))"`; \
(cd lib/$$SUBDIR/lib; tar cf - *so *.so.*) | (cd ../lib/$$SUBDIR; tar xvf - )
clean:
rm -rf ${TAGLIB}
rm -rf lib
Binary file not shown.