diff --git a/.gitignore b/.gitignore index 93e4072..7893359 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ compiled/ *.bak \#* .\#* +libao/c/build diff --git a/libao/c/ao-play-async/ao_playasync.c b/libao/c/ao-play-async/ao_playasync.c index c7410c0..ec5ae59 100644 --- a/libao/c/ao-play-async/ao_playasync.c +++ b/libao/c/ao-play-async/ao_playasync.c @@ -1,20 +1,26 @@ #include "ao_playasync.h" -#include #include #include #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; struct _queue_ *next; struct _queue_ *prev; } Queue_t; @@ -25,8 +31,12 @@ typedef struct { ao_device *ao_device; pthread_mutex_t mutex; pthread_t thread; + double at_second; } 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); @@ -38,9 +48,10 @@ 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; + } else { + h->play_queue->prev = NULL; } return q; } @@ -60,7 +71,7 @@ static void add(AO_Handle *h, Queue_t *elem) } } -static Queue_t *new_elem(int command, int buf_len, void *buf) +static Queue_t *new_elem(int command, double at_second, int buf_len, void *buf) { Queue_t *q = (Queue_t *) malloc(sizeof(Queue_t)); void *new_buf; @@ -71,6 +82,7 @@ static Queue_t *new_elem(int command, int buf_len, void *buf) } else { new_buf = NULL; } + q->at_second = at_second; q->buf = new_buf; q->buflen = buf_len; q->command = command; @@ -79,14 +91,19 @@ static Queue_t *new_elem(int command, int buf_len, void *buf) 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); - if (q->buflen != 0) { - free(q->buf); - } - free(q); + del_elem(q); } } @@ -101,9 +118,17 @@ static void *run(void *arg) int has_frames = (handle->play_queue != NULL); if (has_frames) { - + Queue_t *q = get(handle); + handle->at_second = q->at_second; pthread_mutex_unlock(&handle->mutex); + if (q->command == STOP) { + go_on = (1 == 0); + } else { + ao_play(handle->ao_device, q->buf, q->buflen); + } + + del_elem(q); } else { pthread_mutex_unlock(&handle->mutex); usleep(5000); // sleep for 5ms @@ -113,13 +138,35 @@ static void *run(void *arg) return NULL; } +static void get_ao_play(void) +{ + char *lib = "libao.so"; + void *handle = dlopen(lib, RTLD_LAZY); + if (!handle) { + fprintf(stderr, "Cannot open library %s: %s\n", lib, dlerror()); + exit(EXIT_FAILURE); + } + + ao_play = dlsym(handle, "ao_play"); + char *err; + err = dlerror(); + if (err != NULL) { + fprintf(stderr, "Cannot get function ao_play: %s\n", err); + exit(EXIT_FAILURE); + } +} + + void *ao_create_async(void *ao_device_yeah) { + 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; pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; handle->mutex = m; @@ -131,16 +178,32 @@ void *ao_create_async(void *ao_device_yeah) void ao_stop_async(void *ao_handle) { AO_Handle *h = (AO_Handle *) ao_handle; + + pthread_mutex_lock(&h->mutex); clear(h); - Queue_t *q = new_elem(STOP, 0, NULL); + Queue_t *q = new_elem(STOP, 0.0, 0, NULL); add(h, q); + pthread_mutex_unlock(&h->mutex); + + void *retval; + pthread_join(h->thread, &retval); + free(h); } -void ao_play_async(void *ao_handle, int buf_size, void *mem) +void ao_play_async(void *ao_handle, double at_second, int buf_size, void *mem) { AO_Handle *h = (AO_Handle *) ao_handle; - Queue_t *q = new_elem(PLAY, buf_size, mem); + Queue_t *q = new_elem(PLAY, at_second, buf_size, mem); + pthread_mutex_lock(&h->mutex); add(h, q); + pthread_mutex_unlock(&h->mutex); +} + +double ao_is_at_second_async(void *ao_handle) +{ + AO_Handle *h = (AO_Handle *) ao_handle; + return h->at_second; } + diff --git a/libao/c/ao-play-async/ao_playasync.h b/libao/c/ao-play-async/ao_playasync.h index 4bf3c36..4aeb12e 100644 --- a/libao/c/ao-play-async/ao_playasync.h +++ b/libao/c/ao-play-async/ao_playasync.h @@ -3,6 +3,7 @@ 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); +extern void ao_play_async(void *handle, double at_second, int buf_size, void *mem); +extern double ao_is_at_second_async(void *handle); #endif // AO_PLAYASYNC_H diff --git a/libao/libao-async-ffi.rkt b/libao/libao-async-ffi.rkt new file mode 100644 index 0000000..91e0003 --- /dev/null +++ b/libao/libao-async-ffi.rkt @@ -0,0 +1,37 @@ +#lang racket/base + + +(require ffi/unsafe + ffi/unsafe/define + setup/dirs + "../utils/utils.rkt" + ) + +(provide ao_create_async + ao_stop_async + ao_play_async + ao_is_at_second_async + ) + +;(ffi-lib "/usr/local/lib/libao-play-async.so") + +(define-ffi-definer define-libao-async + (ffi-lib "libao-play-async" '("0" #f) + #:get-lib-dirs (lambda () + (cons (build-path ".") (get-lib-search-dirs))) + #:fail (λ () (error "Cannot load libao-play-async")) + )) + +(define _libao-async-handle-pointer (_cpointer 'ao-async-handle)) + +;extern void *ao_create_async(void *ao_device); +(define-libao-async ao_create_async(_fun _pointer -> _libao-async-handle-pointer)) + +;extern void ao_stop_async(void *handle); +(define-libao-async ao_stop_async(_fun _libao-async-handle-pointer -> _void)) + +;extern void ao_play_async(void *handle, double at_second, int buf_size, void *mem); +(define-libao-async ao_play_async(_fun _libao-async-handle-pointer _double _uint32 _pointer -> _void)) + +;extern double ao_is_at_second_async(void *handle); +(define-libao-async ao_is_at_second_async(_fun _libao-async-handle-pointer -> _double)) diff --git a/libao/libao.rkt b/libao/libao.rkt index 798a59f..017d713 100644 --- a/libao/libao.rkt +++ b/libao/libao.rkt @@ -1,6 +1,7 @@ #lang racket/base (require "libao-ffi.rkt" + "libao-async-ffi.rkt" (prefix-in fin: finalizer) ffi/unsafe data/queue @@ -11,6 +12,7 @@ ao-mk-format ao-close ao-default-driver-id + ao-at-second ) (define devices (make-hash)) @@ -23,8 +25,7 @@ [byte-format #:auto #:mutable] [channels #:auto #:mutable] [rate #:auto #:mutable] - [buffer #:auto #:mutable] - [player #:auto #:mutable] + [async-player #:auto #:mutable] ) #:auto-value #f ) @@ -84,36 +85,7 @@ (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 - ) - ) - ) + (set-ao-handle-async-player! handle (ao_create_async ao-device)) (hash-set! devices handle-num ao-device) (fin:register-finalizer handle (lambda (handle) @@ -131,15 +103,7 @@ (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)) - )) + (ao_stop_async (ao-handle-async-player handle)) (let ((ao-device (hash-ref devices handle-num #f))) (if (eq? ao-device #f) 'error-ao-device-non-existent @@ -164,7 +128,7 @@ (reverse bytes) bytes)))) -(define (ao-play handle buffer) +(define (ao-play handle at-time-in-s buffer) (let* ((bytes-per-sample (ao-handle-bytes-per-sample handle)) (bits (ao-handle-bits handle)) (channels (ao-handle-channels handle)) @@ -195,11 +159,14 @@ 'filled)) )) (fill 0 0) - (enqueue! (ao-handle-buffer handle) (list 'play-buf audio audio-buf-len)) + (ao_play_async (ao-handle-async-player handle) (exact->inexact at-time-in-s) audio-buf-len audio) ) ) ) +(define (ao-at-second handle) + (ao_is_at_second_async (ao-handle-async-player handle))) + ;(let* ((handle-num (ao-handle-handle-num handle)) ; (ao-device (hash-ref devices handle-num #f))) ; (if (eq? ao-device #f) diff --git a/play-test.rkt b/play-test.rkt index f8a9f1c..65bda67 100644 --- a/play-test.rkt +++ b/play-test.rkt @@ -10,8 +10,21 @@ (define fmt (ao-mk-format 24 48000 2 'big-endian)) (define ao-h (ao-open-live #f fmt)) +(define current-seconds 0) + (define (flac-play frame buffer) - (ao-play ao-h buffer)) + (let* ((sample (hash-ref frame 'number)) + (rate (hash-ref frame 'sample-rate)) + (second (/ (* sample 1.0) (* rate 1.0))) + ) + (ao-play ao-h second buffer) + ) + (let ((s (inexact->exact (round (ao-at-second ao-h))))) + (when (> s current-seconds) + (set! current-seconds s) + (displayln (format "At second: ~a" (ao-at-second ao-h))) + )) + ) (define (flac-meta meta) (displayln meta))