From 869fa8739f63d126b38f8874c58ab6bff9cf5473 Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Sun, 22 Feb 2026 16:08:02 +0100 Subject: [PATCH] - --- libao/c/Makefile | 11 ++ libao/c/ao-play-async/.gitignore | 74 ++++++++++++++ libao/c/ao-play-async/CMakeLists.txt | 13 +++ libao/c/ao-play-async/ao_playasync.c | 146 +++++++++++++++++++++++++++ libao/c/ao-play-async/ao_playasync.h | 8 ++ libao/libao.rkt | 60 +++++++++-- libflac/flac-decoder.rkt | 5 - libflac/flac-definitions.rkt | 6 +- play-test.rkt | 7 +- 9 files changed, 315 insertions(+), 15 deletions(-) create mode 100644 libao/c/Makefile create mode 100644 libao/c/ao-play-async/.gitignore create mode 100644 libao/c/ao-play-async/CMakeLists.txt create mode 100644 libao/c/ao-play-async/ao_playasync.c create mode 100644 libao/c/ao-play-async/ao_playasync.h diff --git a/libao/c/Makefile b/libao/c/Makefile new file mode 100644 index 0000000..dc4ced9 --- /dev/null +++ b/libao/c/Makefile @@ -0,0 +1,11 @@ + +all: + mkdir -p build + cmake -S ao-play-async -B build + (cd build; make) + +install: + (cd build; cp *.so /usr/local/lib) + +clean: + rm -rf build diff --git a/libao/c/ao-play-async/.gitignore b/libao/c/ao-play-async/.gitignore new file mode 100644 index 0000000..4a0b530 --- /dev/null +++ b/libao/c/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/libao/c/ao-play-async/CMakeLists.txt b/libao/c/ao-play-async/CMakeLists.txt new file mode 100644 index 0000000..e20a189 --- /dev/null +++ b/libao/c/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/libao/c/ao-play-async/ao_playasync.c b/libao/c/ao-play-async/ao_playasync.c new file mode 100644 index 0000000..c7410c0 --- /dev/null +++ b/libao/c/ao-play-async/ao_playasync.c @@ -0,0 +1,146 @@ +#include "ao_playasync.h" +#include +#include +#include +#include +#include +#include + +typedef enum { + PLAY = 1, + STOP = 2 +} Command_t; + +typedef struct _queue_ { + Command_t command; + void *buf; + int buflen; + struct _queue_ *next; + struct _queue_ *prev; +} Queue_t; + +typedef struct { + Queue_t *play_queue; + Queue_t *last_frame; + ao_device *ao_device; + pthread_mutex_t mutex; + pthread_t thread; +} AO_Handle; + +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; + h->play_queue->prev = NULL; + if (h->play_queue == NULL) { + h->last_frame = NULL; + } + 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; + } +} + +static Queue_t *new_elem(int command, 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->buf = new_buf; + q->buflen = buf_len; + q->command = command; + q->next = NULL; + q->prev = NULL; + return q; +} + +static void clear(AO_Handle *h) +{ + while (h->play_queue != NULL) { + Queue_t *q = get(h); + if (q->buflen != 0) { + free(q->buf); + } + free(q); + } +} + +static void *run(void *arg) +{ + AO_Handle *handle = (AO_Handle *) arg; + + int go_on = (1 == 1); + + while(go_on) { + pthread_mutex_lock(&handle->mutex); + int has_frames = (handle->play_queue != NULL); + + if (has_frames) { + + pthread_mutex_unlock(&handle->mutex); + + } else { + pthread_mutex_unlock(&handle->mutex); + usleep(5000); // sleep for 5ms + } + } + + return NULL; +} + +void *ao_create_async(void *ao_device_yeah) +{ + 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; + pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; + handle->mutex = m; + + pthread_create(&handle->thread, NULL, run, handle); + + return (void *) handle; +} + +void ao_stop_async(void *ao_handle) +{ + AO_Handle *h = (AO_Handle *) ao_handle; + clear(h); + Queue_t *q = new_elem(STOP, 0, NULL); + add(h, q); +} + +void ao_play_async(void *ao_handle, int buf_size, void *mem) +{ + AO_Handle *h = (AO_Handle *) ao_handle; + Queue_t *q = new_elem(PLAY, buf_size, mem); + add(h, q); +} + + diff --git a/libao/c/ao-play-async/ao_playasync.h b/libao/c/ao-play-async/ao_playasync.h new file mode 100644 index 0000000..4bf3c36 --- /dev/null +++ b/libao/c/ao-play-async/ao_playasync.h @@ -0,0 +1,8 @@ +#ifndef AO_PLAYASYNC_H +#define AO_PLAYASYNC_H + +extern void *ao_create_async(void *ao_handle); +extern void ao_stop_async(void *handle); +extern void ao_play_async(void *handle, int buf_size, void *mem); + +#endif // AO_PLAYASYNC_H diff --git a/libao/libao.rkt b/libao/libao.rkt index 8136aed..798a59f 100644 --- a/libao/libao.rkt +++ b/libao/libao.rkt @@ -2,7 +2,9 @@ (require "libao-ffi.rkt" (prefix-in fin: finalizer) - ffi/unsafe) + ffi/unsafe + data/queue + ) (provide ao-open-live ao-play @@ -21,6 +23,8 @@ [byte-format #:auto #:mutable] [channels #:auto #:mutable] [rate #:auto #:mutable] + [buffer #:auto #:mutable] + [player #:auto #:mutable] ) #:auto-value #f ) @@ -80,6 +84,36 @@ (set-ao-handle-byte-format! handle endianness) (set-ao-handle-rate! handle rate) (set-ao-handle-channels! handle channels) + (let ((buf (make-queue))) + (set-ao-handle-buffer! handle buf) + (set-ao-handle-player! handle + (thread (λ () + (let ((samples-per-time 10) + (sample 0) + ) + (define (play) + (displayln (format "Queue-length: ~a" (queue-length buf))) + (if (queue-empty? buf) + (begin + (sleep 0.1) + (play)) + (let ((front (dequeue! buf))) + (if (eq? (car front) 'close) + 'closed + (let ((audio (cadr front)) + (audio-buf-len (caddr front))) + (ao_play ao-device audio audio-buf-len) + (play)) + ) + ) + ) + ) + (play)) + ) + #:pool 'own + ) + ) + ) (hash-set! devices handle-num ao-device) (fin:register-finalizer handle (lambda (handle) @@ -97,6 +131,15 @@ (printf "Unexpected: cannot close ao-device")))) 'internally-closed) (let ((handle-num (ao-handle-handle-num handle))) + (let ((q (ao-handle-buffer handle))) + (letrec ((emptier (λ () (if (queue-empty? q) + 'done + (begin + (dequeue! q) + (emptier)))))) + (emptier) + (enqueue! q (list 'close #f #f)) + )) (let ((ao-device (hash-ref devices handle-num #f))) (if (eq? ao-device #f) 'error-ao-device-non-existent @@ -152,11 +195,16 @@ 'filled)) )) (fill 0 0) - (let* ((handle-num (ao-handle-handle-num handle)) - (ao-device (hash-ref devices handle-num #f))) - (if (eq? ao-device #f) - (error "No device for this handle") - (ao_play ao-device audio audio-buf-len)))))) + (enqueue! (ao-handle-buffer handle) (list 'play-buf audio audio-buf-len)) + ) + ) + ) + +;(let* ((handle-num (ao-handle-handle-num handle)) +; (ao-device (hash-ref devices handle-num #f))) +; (if (eq? ao-device #f) +; (error "No device for this handle") +; (ao_play ao-device audio audio-buf-len)))))) diff --git a/libflac/flac-decoder.rkt b/libflac/flac-decoder.rkt index cd3a13e..3737df8 100644 --- a/libflac/flac-decoder.rkt +++ b/libflac/flac-decoder.rkt @@ -10,7 +10,6 @@ flac-read-meta flac-stream-state (all-from-out "flac-definitions.rkt") - test-file test-file2 test-file3 kinds last-buffer last-buf-len ) @@ -124,8 +123,4 @@ (flac-handle-stream-info handle)) #f))) - (define test-file "C:/devel/racket/racket-sound/libflac/capr24.flac") - (define test-file2 "C:/Muziek/Klassiek-Kamermuziek/Beethoven/Rachel Podger/01 Violin Sonata No. 1 in D Major, Op. 12 No. 1- I. Allegro con brio.flac") - (define test-file3 "c:/Muziek/Pop/Radiohead/The Best of Radiohead (2008)/02. Paranoid Android.flac") - ); end of module diff --git a/libflac/flac-definitions.rkt b/libflac/flac-definitions.rkt index f0dfa73..139ca38 100644 --- a/libflac/flac-definitions.rkt +++ b/libflac/flac-definitions.rkt @@ -23,14 +23,16 @@ flac-duration ) - (define-struct flac-stream-info + (define-struct flac-stream-info (min-blocksize max-blocksize min-framesize max-framesize sample-rate channels bits-per-sample total-samples - )) + ) + #:transparent + ) (define (flac-stream-info->string si) (format "sample-rate: ~a, channels: ~a, bits-per-sample: ~a, total-samples: ~a" diff --git a/play-test.rkt b/play-test.rkt index a0f730d..f8a9f1c 100644 --- a/play-test.rkt +++ b/play-test.rkt @@ -1,11 +1,13 @@ #lang racket/base (require "libao/libao.rkt" "libflac/flac-decoder.rkt" + data/queue ) -(define fmt (ao-mk-format 16 44100 2 'big-endian)) +(define test-file3 "/muziek/Klassiek-Viool/Alina Ibragimova/Paganini_24 Caprices (2021)/24. 24 Caprices, Op 1 - No. 24 in A minor- Tema con variazioni. Quasi presto.flac") +(define fmt (ao-mk-format 24 48000 2 'big-endian)) (define ao-h (ao-open-live #f fmt)) (define (flac-play frame buffer) @@ -14,7 +16,8 @@ (define (flac-meta meta) (displayln meta)) -(define flac-h (flac-open test-file3 flac-meta flac-play)) +(define flac-h + (flac-open test-file3 flac-meta flac-play)) (flac-read flac-h)