libao
1 Audio handles
ao-handle?
ao-valid?
ao-device-bits
2 Validation predicates
ao-valid-bits?
ao-valid-rate?
ao-valid-channels?
ao-valid-format?
ao-supported-music-format?
3 Opening and closing
ao-open-live
ao-open-file
ao-close
4 Playback
ao-play
ao-pause
ao-clear-async
5 Playback state
ao-at-second
ao-at-music-id
ao-music-duration
ao-bufsize-async
6 Volume control
ao-set-volume!
ao-volume
7 Notes
9.1

libao🔗ℹ

Hans Dijkema <hans@dijkewijk.nl>

 (require racket-audio/libao) package: racket-audio

This module provides a small high-level interface to an asynchronous audio output backend. It opens a live output device or a file output, queues audio buffers for playback, reports playback position, supports pause and buffer clearing, and exposes a small set of validation predicates.

The central value is an ao-handle, created by ao-open-live or ao-open-file. An ao-handle stores the requested playback configuration together with a native asynchronous player handle. It also records the real bit depth accepted by the selected libao output device.

1 Audio handles🔗ℹ

procedure

(ao-handle? v)  boolean?

  v : any/c
Returns #t if v is an ao-handle value, and #f otherwise.

procedure

(ao-valid? handle)  boolean?

  handle : ao-handle?
Returns #t if handle still has a native asynchronous player, and #f otherwise.

A handle becomes invalid after ao-close, or when opening the native player failed.

procedure

(ao-device-bits handle)  integer?

  handle : ao-handle?
Returns the real bit depth of the opened output device.

This can differ from the bit depth requested with ao-open-live or ao-open-file. For example, when 32-bit output is requested but the libao driver only accepts 24-bit output, this function returns 24.

2 Validation predicates🔗ℹ

procedure

(ao-valid-bits? bits)  boolean?

  bits : any/c
Returns #t if bits is one of 8, 16, 24, or 32, and #f otherwise.

procedure

(ao-valid-rate? rate)  boolean?

  rate : any/c
Returns #t if rate is one of the sample rates accepted by this module, and #f otherwise.

The accepted rates are:

  • 8000, 11025, 16000, 22050

  • 44100, 48000, 88200, 96000

  • 176400, 192000, 352800, 384000

procedure

(ao-valid-channels? channels)  boolean?

  channels : any/c
Returns #t if channels is an integer greater than or equal to 1, and #f otherwise.

procedure

(ao-valid-format? format)  boolean?

  format : any/c
Returns #t if format is one of 'little-endian, 'big-endian, or 'native-endian, and #f otherwise.

procedure

(ao-supported-music-format? format)  boolean?

  format : any/c
Returns #t if format is one of 'ao or 'flac, and #f otherwise.

The symbol does not describe an encoded audio format. It describes the in-memory layout of the PCM buffer passed to ao-play. 'ao means interleaved PCM samples. 'flac means channel-oriented PCM samples, as produced by the FLAC decoder, which must be converted to interleaved PCM before playback.

3 Opening and closing🔗ℹ

procedure

(ao-open-live bits rate channels byte-format)  ao-handle?

  bits : ao-valid-bits?
  rate : ao-valid-rate?
  channels : ao-valid-channels?
  byte-format : ao-valid-format?
Creates an audio output handle for live playback.

This is equivalent to calling ao-open-file with #f as the filename.

The handle stores the requested sample size, sample rate, channel count, and byte format. The native backend first tries to open the device with the requested bit depth. If that fails, it may fall back to a lower bit depth accepted by the selected libao driver.

The requested bit depth describes the buffers supplied by the Racket side. The real device bit depth describes the format accepted by libao and can be inspected with ao-device-bits.

If the native player is created successfully, the returned handle is valid. If player creation fails, the function still returns an ao-handle, but that handle is marked closed and is not valid for playback.

A finalizer is registered for the handle and calls ao-close when the handle is reclaimed.

procedure

(ao-open-file bits    
  rate    
  channels    
  byte-format    
  filename)  ao-handle?
  bits : ao-valid-bits?
  rate : ao-valid-rate?
  channels : ao-valid-channels?
  byte-format : ao-valid-format?
  filename : (or/c path? string? #f)
Creates an audio output handle.

If filename is #f, the default live libao output device is opened. Otherwise the native backend opens a file output target using the given filename.

The requested bit depth is stored in the handle and describes the input buffers that will be queued with ao-play. The native backend also records the real bit depth accepted by the output device or file backend. Use ao-device-bits to inspect that value.

procedure

(ao-close handle)  void?

  handle : ao-handle?
Stops playback for handle and releases the native player reference stored in the handle.

If the handle already has no native player, this procedure has no effect.

4 Playback🔗ℹ

procedure

(ao-play handle    
  music-id    
  at-time-in-s    
  music-duration-s    
  buffer    
  buf-len    
  buf-type)  void?
  handle : ao-handle?
  music-id : integer?
  at-time-in-s : number?
  music-duration-s : number?
  buffer : any/c
  buf-len : integer?
  buf-type : ao-supported-music-format?
Queues audio data for asynchronous playback.

The music-id argument identifies the music stream associated with the buffer. The arguments at-time-in-s and music-duration-s describe the position and duration, in seconds, associated with the buffer. The arguments buffer and buf-len provide the audio data and its length. The buf-type argument specifies the in-memory PCM layout.

The buffer description passed to the native layer is completed with the requested sample size, sample rate, channel count, and byte format stored in handle.

Two buffer layouts are supported:

  • 'ao: interleaved PCM samples, for example L0 R0 L1 R1.

  • 'flac: channel-oriented PCM samples, for example one channel buffer for left samples and one channel buffer for right samples.

The native backend converts 'flac buffers to interleaved PCM before playback. It also converts between the requested bit depth and the real device bit depth when needed. This makes it possible to keep decoder output at 32-bit signed integer PCM while still playing on devices that only accept 24-bit or 16-bit integer samples.

The queued buffer is copied by the native backend, so the caller does not need to keep the original buffer alive after ao-play returns.

If handle is not valid, this procedure raises an exception.

procedure

(ao-pause handle pause)  void?

  handle : ao-handle?
  pause : boolean?
Pauses or resumes asynchronous playback for handle.

A true value pauses playback. #f resumes playback.

procedure

(ao-clear-async handle)  void?

  handle : ao-handle?
Clears buffered asynchronous playback data for handle.

5 Playback state🔗ℹ

procedure

(ao-at-second handle)  number?

  handle : ao-handle?
Returns the current playback position, in seconds, as reported by the native asynchronous player.

procedure

(ao-at-music-id handle)  integer?

  handle : ao-handle?
Returns the music identifier currently reported by the native asynchronous player.

procedure

(ao-music-duration handle)  number?

  handle : ao-handle?
Returns the duration of the current music stream, in seconds, as reported by the native asynchronous player.

procedure

(ao-bufsize-async handle)  integer?

  handle : ao-handle?
Returns the current buffered size in bytes for the asynchronous player.

6 Volume control🔗ℹ

procedure

(ao-set-volume! handle percentage)  void?

  handle : ao-handle?
  percentage : number?
Sets the playback volume for handle.

If percentage is an exact integer, it is converted to an inexact number before it is passed to the native layer.

procedure

(ao-volume handle)  number?

  handle : ao-handle?
Returns the current playback volume as reported by the native asynchronous player.

7 Notes🔗ℹ

This module is a higher-level wrapper around the asynchronous FFI layer. It stores the playback configuration in the handle, and reuses that configuration for each call to ao-play.

The requested bit depth and the real device bit depth are deliberately kept separate. The requested value describes the buffers supplied by the Racket side. The real value describes the format accepted by libao.

The module does not expose the handle fields directly. The public API is intentionally small: create a handle, queue buffers, inspect position and buffer state, pause or clear playback, adjust volume, and close the handle.

A typical usage pattern is to open one live handle for a given stream format, queue decoded buffers with ao-play, and query the playback position with ao-at-second while playback proceeds asynchronously.