ffi.rkt
#lang racket/base

(require 
 (for-syntax "make-sample-format.rkt")
 (prefix-in sf: "sample-format.rkt")
 ffi/unsafe 
 (for-syntax racket/base))

(define lib (ffi-lib "libao" "4"))

(define-syntax (define-ao form)
  (syntax-case form ()
    ((_ name type)
     (let* ((datum (syntax-e form))
            (name (cadr datum))
            (name-datum (syntax->datum name))
            (type (caddr datum)))
       (when (not (symbol? name-datum))
         (error "The name must be a symbol, got " name-datum))
       (let ((c-name (string-append "ao_" (regexp-replace* #rx"-" (symbol->string name-datum) "_"))))
         (datum->syntax
          form
          `(begin
             (define ,name (get-ffi-obj ,c-name lib ,type))
             (provide ,name))
          form))))))

(define-ao initialize (_fun -> _void))
(define-ao shutdown (_fun -> _void))

(define-ao default-driver-id (_fun -> _int))
(define-ao driver-id (_fun _string -> _int))

(define-cpointer-type _options)
(define-cpointer-type _device)

(define _sample_format _gcpointer)

(define-ao append-option (_fun (options key value) ::
                               (options : (_ptr io _options/null) = options)
                               (key : _string)
                               (value : _string)
                               -> (result : _int)
                               -> (values (= result 1) options)))

(define-ao open-live (_fun _int _sample_format _options/null -> _device))
(define-ao open-file (_fun (id filename overwrite format options) ::
                           (id : _int) 
                           (filename : _string)
                           (_int = (if overwrite 1 0))
                           (format : _sample_format)
                           (options : _options/null)
                           -> _device))

(define-ao play (_fun (device samples) ::
                      (device : _device)
                      (samples : _bytes)
                      (_uint32 = (bytes-length samples))
                      -> (result : _int) 
                      -> (not (= result 0))))

(define-ao close (_fun _device -> (result : _int) -> (= result 1)))

(provide (rename-out (sf:build build-sample-format)
                     (sf:little-endian little-endian)
                     (sf:big-endian big-endian)
                     (sf:native-endian native-endian)))