Cauldron2D.Audio (Cauldron2D v0.1.3)

Copy Markdown View Source

Sound effects and music for one listener, over one TuningFork stage.

A game emits the events that happened — :brick, {:explosion, {x, y}} — and supplies a function from an event's name to the voice it makes. Each instance is a process; a game with one player uses the default one, a served game starts one per session with a sink that reaches that player.

Cauldron2D.Audio.start(sounds: &MyGame.Sound.voice/1, sfx: 0.85, music: 0.35)
Cauldron2D.Audio.play([:brick, {:wall, {12.0, 3.0}}])
Cauldron2D.Audio.bed(Cauldron2D.Audio, "mygame/level-3", fn -> MyGame.Music.render(3) end)

{:ok, audio} = Cauldron2D.Audio.start_link(name: nil, sink: {TuningFork.Sink.Pulse, server: "tcp:127.0.0.1:<port>"})
Cauldron2D.Audio.listener(audio, {player_x, player_y})
Cauldron2D.Audio.play(audio, events)

Every function takes the instance first. The ones with a fixed number of arguments default it to the process registered as Cauldron2D.Audio; play_piece/4 and bed/4 take it explicitly.

Rules

  • start/1 or start_link/1 comes first. It records the sound table and opens the stage; when this machine has no sound the stage stays silent and every call is a no-op returning :ok. on?/1 reports which.
  • An event is any term the table knows, or {event, {x, y}} placing it. With a listener set, a placed event is scaled by its distance — full within one tile, silent at :earshot tiles — and panned by its side; an unplaced one plays at full level, centred.
  • Effects and music have separate levels, set by levels/3.
  • Beds are keyed by a string. A render runs once, in a task, and is kept in memory and on disk via TuningFork.Cache; a bed finishing after another key was asked for is dropped. A render that raises is logged and never plays.
  • clear_bed/1 and resume_bed/1 cover both playback modes.
  • Music with sections and layers is Cauldron2D.Audio.Music.

Summary

Functions

Loop the PCM render produces under key; see the module rules for caching.

Returns a specification to start this module under a supervisor.

Stop the music, live or rendered.

The effects and music levels an instance starts with: %{sfx: 0.85, music: 0.35}.

Set the effects and music levels, each 0.0 to 1.0.

Where the listener is, for placed events.

Silence everything, or restore it.

Whether a stage with somewhere to play is running.

Sound events in order through the table given at start: a name — any term — or {name, {x, y}} placed against the listener; one the table answers nil for, or has no clause for, is silent.

Play score on a loop, live or from a render, under key.

Sound voice directly, scaled by the effects level.

The key last given to bed/4 or play_piece/4, or nil.

Start the music last asked for again.

The stage this instance plays through, or nil.

Start the default instance, registered as Cauldron2D.Audio, replacing one already running. Returns whether sound is possible.

Start an instance linked to the caller.

Types

event()

@type event() :: term() | {term(), {number(), number()}}

instance()

@type instance() :: GenServer.server()

sound()

@type sound() ::
  TuningFork.Voice.t() | [{non_neg_integer(), TuningFork.Voice.t()}] | nil

Functions

bed(audio, key, render, opts \\ [])

@spec bed(instance(), String.t(), (-> binary()), keyword()) :: :ok

Loop the PCM render produces under key; see the module rules for caching.

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

clear_bed(audio \\ __MODULE__)

@spec clear_bed(instance()) :: :ok

Stop the music, live or rendered.

default_levels()

@spec default_levels() :: %{sfx: float(), music: float()}

The effects and music levels an instance starts with: %{sfx: 0.85, music: 0.35}.

levels(audio \\ __MODULE__, sfx, music)

@spec levels(instance(), number(), number()) :: :ok

Set the effects and music levels, each 0.0 to 1.0.

listener(audio \\ __MODULE__, pos)

@spec listener(instance(), {number(), number()} | nil) :: :ok

Where the listener is, for placed events.

mute(audio \\ __MODULE__, muted?)

@spec mute(instance(), boolean()) :: :ok

Silence everything, or restore it.

on?(audio \\ __MODULE__)

@spec on?(instance()) :: boolean()

Whether a stage with somewhere to play is running.

play(audio \\ __MODULE__, events)

@spec play(instance(), [event()]) :: :ok

Sound events in order through the table given at start: a name — any term — or {name, {x, y}} placed against the listener; one the table answers nil for, or has no clause for, is silent.

play_piece(audio, key, score, opts \\ [])

@spec play_piece(instance(), String.t(), TuningFork.Score.t(), keyword()) :: :ok

Play score on a loop, live or from a render, under key.

Options

  • :mode:live or :rendered, overriding the instance's default
  • :render — a zero-arity function returning the PCM to loop in :rendered mode. Default: renders score at the stage's rate
  • :fingerprint — keys the on-disk render together with key. Default "0"

play_voice(audio \\ __MODULE__, voice)

@spec play_voice(instance(), TuningFork.Voice.t()) :: :ok

Sound voice directly, scaled by the effects level.

playing(audio \\ __MODULE__)

@spec playing(instance()) :: String.t() | nil

The key last given to bed/4 or play_piece/4, or nil.

resume_bed(audio \\ __MODULE__)

@spec resume_bed(instance()) :: :ok

Start the music last asked for again.

stage(audio \\ __MODULE__)

@spec stage(instance()) :: pid() | nil

The stage this instance plays through, or nil.

start(opts \\ [])

@spec start(keyword()) :: boolean()

Start the default instance, registered as Cauldron2D.Audio, replacing one already running. Returns whether sound is possible.

Takes the options of start_link/1.

start_link(opts \\ [])

@spec start_link(keyword()) :: GenServer.on_start()

Start an instance linked to the caller.

The instance lives as long as the caller: when the caller exits, for any reason, the instance stops, and a stage it started stops with it, closing its sink. A stage handed in with :stage is left running.

Options

  • :name — a registered name, or nil for none. Default Cauldron2D.Audio
  • :sounds — a function from an event name to a sound/0. Default: silent
  • :sink — a TuningFork.Sink module, or {module, sink_opts}. Default: TuningFork.Sink.Speaker when that package is present, else nothing plays
  • :stage — an already running stage to use instead of starting one
  • :mode — the default for play_piece/4, :live or :rendered. Default :live
  • :sfx, :music — levels 0.0 to 1.0. Default default_levels/0
  • :earshot — tiles at which a placed event falls silent. Default 30.0
  • :music_dir — the directory rendered music is kept in; a game's is Cauldron2D.Client.Game.music_dir/1. Default TuningFork.Cache.dir/0
  • :static_music — play the first section cued and nothing after, rendered once and looped; see Cauldron2D.Audio.Music. Default false
  • :parallel — synthesise the music's voices on every core, for one player on a machine of their own (the terminal, the desktop); a server with many players leaves it off. Default false
  • :rate, :channels, :chunk, :voices, :lead, :fx — the stage's settings. Default 44_100, 2, 256, 12, 2048, []: the sink is kept 2048 frames (46 ms at 44.1 kHz) ahead of playback, in a ring of four times that, so a few milliseconds of scheduling jitter never leave it empty