#lang racket
(require "portaudio.rkt")
(provide (contract-out
[default-host-api (-> symbol?)]
[all-host-apis (-> (listof symbol?))]
[host-api (parameter/c (or/c false? symbol?))]
[find-output-device (-> number? nat?)]
[device-low-output-latency (-> nat? number?)]))
(define nat? exact-nonnegative-integer?)
(define (default-host-api)
(define host-api-index (pa-get-default-host-api))
(pa-host-api-info-type (pa-get-host-api-info host-api-index)))
(define (all-host-apis)
(for/list ([i (in-range (pa-get-host-api-count))])
(pa-host-api-info-type (pa-get-host-api-info i))))
(define host-api
(make-parameter
#f
(lambda (new-val)
(cond [(not (or (false? new-val)
(member new-val (all-host-apis))))
(raise-argument-error 'host-api "false or host api symbol"
0 new-val)]
[else new-val]))))
(define (find-output-device latency)
(define selected-host-api (or (host-api) (default-host-api)))
(define reasonable-devices
(low-latency-output-devices latency selected-host-api))
(when (null? reasonable-devices)
(error 'stream-choose "no devices available in current API ~s with ~sms latency or less."
selected-host-api
(* 1000 latency)))
(define default-output-device (host-api-default-output-device
selected-host-api))
(cond [(member default-output-device reasonable-devices)
default-output-device]
[else
(log-info
(format
"default output device doesn't support low-latency (~sms) output, using device ~s instead"
(* 1000 latency)
(device-name (car reasonable-devices))))
(car reasonable-devices)]))
(define (host-api-default-output-device host-api)
(for/or ([i 0]
[this-host-api (all-host-apis)]
#:when (symbol=? host-api this-host-api))
i))
(define (low-latency-output-devices latency host-api)
(for/list ([i (in-range (pa-get-device-count))]
#:when (belongs-to-selected-api? host-api i)
#:when (has-outputs? i)
#:when (matches-latency? latency i))
i))
(define (belongs-to-selected-api? selected-host-api i)
(define device-host-api-index
(pa-device-info-host-api (pa-get-device-info i)))
(define host-api (pa-host-api-info-type (pa-get-host-api-info
device-host-api-index)))
(symbol=? selected-host-api host-api))
(define (has-outputs? i)
(<= 2 (pa-device-info-max-output-channels (pa-get-device-info i))))
(define (matches-latency? latency i)
(<= (device-low-output-latency i) latency))
(define (device-low-output-latency i)
(pa-device-info-default-low-output-latency (pa-get-device-info i)))