Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 64 additions & 2 deletions docs/Scripting/API/audio.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Play engine-decoded WAV, MP3, AAC, FLAC, and OGG voices, adjust parameters and s

These engine-owned services work in Lua 5.4, Luau, JavaScript, Python, C, C++, C#, Java, PHP, and HTML inline JavaScript. Follow [Use gameplay actions](/docs/guides/gameplay-actions) to create and attach a script, install external toolchains, and choose the correct language binding. PHP and HTML scripts belong under `ui/`; CSS alone runs no gameplay code.

The Calls section uses JavaScript signatures. The function examples below include native syntax for all ten script types, including SDK-specific alternatives and limitations. Attach a JavaScript Object Component script to the specified target and press **Play**. Globals are supplied by the engine. Schedule actions from `Start` or a callback, rather than at script top level. Native languages use typed options and their own builder conventions; see the guide's language bindings.
The signatures and complete example below use JavaScript. Attach a JavaScript Object Component script to the specified target and press **Play**. Globals are supplied by the engine. Schedule actions from `Start` or a callback, rather than at script top level. Native languages use typed options and their own builder conventions; see the guide's language bindings.

## Supported audio files

Expand Down Expand Up @@ -91,8 +91,70 @@ The object methods and `Audio.load` are available in Lua 5.4 and Luau. Existing
`Audio.play(source, options?)` and `Audio.playAt` in all languages still decode
and play immediately; their voice controls continue to work.

## Choose audio devices

The four device methods are available on `Audio` in every supported script
language (including HTML inline JavaScript). Native enumeration and output
selection currently work in the Windows player. Other platforms return an empty
list and report an unavailable-backend error when selecting a device.

- `Audio.get_output_devices()` lists playback devices.
- `Audio.get_input_devices()` lists microphone/input devices.
- Each entry contains an opaque string `id` and a display `name`. Names can be
duplicated; use the ID to select a device.
- `Audio.set_output_device(id)` switches this scene's mixed audio to that output.
- `Audio.set_input_device(id)` validates and retains the scene's selected input
device. **Microphone recording/capture is not implemented**, so this call does
not produce microphone samples or route microphone sound to playback.
Comment on lines +106 to +108

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Qualify the claim that microphone capture is unavailable

For Windows Lua/Luau users, this unqualified statement says microphone recording and capture are not implemented, but the same page later documents working microphone recording via startRecording(..., {source = "microphone"}) and live capture via startHotMic(). Clarify that set_input_device does not itself start capture, or that the selected ID is not yet consumed by those APIs, rather than denying the documented capture functionality.

AGENTS.md reference: AGENTS.md:L3-L6

Useful? React with 👍 / 👎.

- Pass `null` (Lua/Luau `nil`, Python `None`, C `NULL`) to either setter to select
the current system default. Java requires an explicit `null` argument; other
bindings except C also allow omitting the argument. Setters return no value.
- Lists are snapshots. IDs belong to the current Play scene and the latest list
for that direction; listing that direction again invalidates older IDs. Do
not save IDs in project settings. Selection is shared by scripts in the scene
and resets on Stop/new Play. Output and input IDs are not interchangeable.
- Output switching retains mixer voices and their cursors but discards samples
queued on the previous device; a brief interruption is possible. A failed
switch keeps the previous output. Device removal is not automatically recovered;
list again and choose another device or the default.

Create and attach a JavaScript Object Component script to an enabled scene
object as described above. Press **Play** on Windows:

```javascript
globalThis.behavior = {
Start() {
const outputs = Audio.get_output_devices();
if (outputs.length > 0) {
Audio.set_output_device(outputs[0].id);
}
const inputs = Audio.get_input_devices();
if (inputs.length > 0) {
Audio.set_input_device(inputs[0].id);
}
}
};
```

The scene uses the first listed output, and retains the first input when one is
available. Play an existing audio asset to hear the selected output. To restore
system output, call `Audio.set_output_device(null)` from a callback.

Dynamic bindings return arrays/lists/tables of device records. C returns
`RusticAudioDevices` with `count` and `items[i].id` / `items[i].name`; these values
last until the next callback. C++ returns a `RusticValue` array (`.array()`);
C# returns a `JsonElement` array (`EnumerateArray()`); Java returns a JSON list
of maps; PHP returns an array of associative arrays.

Unknown/stale IDs, IDs from the other direction, unavailable devices, missing
system defaults and native driver failures raise a script error. A non-string
argument also raises an error. Check the Console; list devices again after
Comment on lines +149 to +151

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exempt null from the invalid-argument warning

When callers reset either selection to the current system default, the preceding API instructions explicitly require null (or its language equivalent), but this blanket statement says every non-string argument raises an error. Since null is not a string, this contradicts the documented reset path; qualify the failure case as a non-string, non-null argument.

Useful? React with 👍 / 👎.

plugging/unplugging hardware. Empty lists are valid on hosts without devices.

## Calls

- `Audio.get_output_devices() / get_input_devices()`
- `Audio.set_output_device(id = null) / set_input_device(id = null)`
Comment on lines +156 to +157

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add generated examples for the new device calls

These additions make the required npm run check:api-examples CI step fail: scripts/check-api-examples.mjs extracts qualified calls from this section, but the audio entries in lib/api-example-data.json contain neither Audio.get_output_devices nor Audio.set_output_device, producing No example for published call audio/Audio.get_output_devices. Add all four device APIs to the generated per-language examples so the published reference remains complete and the quality workflow can pass.

Useful? React with 👍 / 👎.

Comment on lines +156 to +157

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Register the device methods in the API reference

These calls are added to the Markdown but not to the audio category in lib/api-reference.ts. Because docGroups converts api/audio to an api-category: source and buildApiCategoryMarkdown renders that route solely from apiCategories, the entire new device section and all four methods are absent from the published /docs/api/audio page, per-call routes, navigation, and search. Independently of adding the generated language examples, add the four signatures and the renderer metadata needed for their call pages.

Useful? React with 👍 / 👎.

- `Audio.load(source)` (Lua/Luau; returns a reusable audio handle)
- `Audio.play(source, options?)`
- `Audio.playAt(source, position, options?)`
Expand Down Expand Up @@ -129,7 +191,7 @@ globalThis.behavior = {
## Behavior and limitations

- Provide assets/music.ogg before Play. The voice fades in over two seconds, starts fading out at five seconds and stops at six.
- Windows player output uses the default audio device. Other platforms currently mix PCM headlessly, so successful playback may produce no device sound.
- Windows player output starts on the default audio device; `Audio.set_output_device` can change it. Other platforms currently mix PCM headlessly, so successful playback may produce no device sound.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update the playback guide for switched outputs

When a script calls Audio.set_output_device, this new text correctly says playback can leave the system default, but the separately published docs/Scripting/gameplayActions/audio.md guide still states at lines 41–42 that Windows output feeds the default audio device. Update that guide as part of this change so users do not receive contradictory routing behavior depending on which audio page they read.

Useful? React with 👍 / 👎.

- FadeOut reaches zero volume but does not stop the voice; stop explicitly or allow natural completion. Crossfade fades the old voice to zero and the new voice from zero to one.
- PlayAt attenuates distance relative to the active camera. The mixer renders 48 kHz stereo PCM with linear resampling for pitch.
- Clock scaling changes source playback rate; Clock.pause and voice.pause retain their source cursors. Natural completion releases non-looping voice PCM; loaded audio handles retain PCM until unload or owner cleanup. Stopping cancels voice-targeting actions on the next scheduler tick.
Expand Down
2 changes: 1 addition & 1 deletion lib/api-docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const docs: Record<string, ApiDoc> = {
gameplay: {
title: "Shared gameplay actions (API 1.1)", summary: "Shared easing, tweens, movement, clip and procedural animation, timelines, timers, paths, cameras, physics, effects, audio and signals in all ten script types.",
when: "Start an action during Start or an event callback. The scene advances it once per frame, even if the behavior has no Update function. Follow [Use gameplay actions](/docs/guides/gameplay-actions) for attachment, complete signatures and backend limits.",
calls: ["Tween.to / move / rotate / scale / value", "Movement.move / moveTo / rotateTo / lookAt / follow / orbit", "Animation.play / blend / transition / register / addMarker / value / ik", "Sequence / Timeline: move / to / wait / call / animation / parallel / play", "Timer.after / every", "Smooth / Interpolation: lerp / slerp / inverseLerp / remap / smoothDamp", "Path.create / follow", "Camera.moveTo / zoom / lookAt / follow / orbit / shake / transition", "Physics.raycast / sphereCast / overlap / force / impulse / explosion / knockback / launch", "Effects.fade / flash / shake / pulse", "Audio.load (Lua/Luau) / play / playAt / fadeIn / fadeOut / crossfade / volume / pitch", "rustic.game:startRecording / stopRecording / startHotMic / stopHotMic (Lua/Luau)", "Events.on / once / emit / connect / disconnect", "Clock.timeScale / pause / resume", "handle.state / pause / resume / cancel / reverse / onFinished"],
calls: ["Tween.to / move / rotate / scale / value", "Movement.move / moveTo / rotateTo / lookAt / follow / orbit", "Animation.play / blend / transition / register / addMarker / value / ik", "Sequence / Timeline: move / to / wait / call / animation / parallel / play", "Timer.after / every", "Smooth / Interpolation: lerp / slerp / inverseLerp / remap / smoothDamp", "Path.create / follow", "Camera.moveTo / zoom / lookAt / follow / orbit / shake / transition", "Physics.raycast / sphereCast / overlap / force / impulse / explosion / knockback / launch", "Effects.fade / flash / shake / pulse", "Audio.get_output_devices / get_input_devices / set_output_device / set_input_device / load (Lua/Luau) / play / playAt / fadeIn / fadeOut / crossfade / volume / pitch", "rustic.game:startRecording / stopRecording / startHotMic / stopHotMic (Lua/Luau)", "Events.on / once / emit / connect / disconnect", "Clock.timeScale / pause / resume", "handle.state / pause / resume / cancel / reverse / onFinished"],
parameters: [p("entity", "stable entity ID / resolved path", true, "Transform target; use an anchored Part to avoid competing gravity."), p("duration", "finite non-negative seconds", true, "Time to completion. Movement and tween helpers also provide duration or speed options."), p("easing", "Ease identifier", false, "Linear by default; all 31 shared curves are available.")],
returns: "An operation handle. Completion is queued during Update. Cancellation and destroyed targets do not invoke completion.",
examples: {
Expand Down
1 change: 1 addition & 0 deletions lib/docs-catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ const categoryGroups: DocGroup[] = [
{ slug: "api/physics-actions/queries", title: "Physics queries", description: "Find primitive colliders with raycasts, sphere casts and overlaps.", source: "docs/Scripting/API/physics-actions/queries.md", parent: "api/physics-actions" },
{ slug: "api/physics-actions/forces", title: "Physics forces", description: "Apply forces, impulses and velocity changes to simulated bodies.", source: "docs/Scripting/API/physics-actions/forces.md", parent: "api/physics-actions" },
{ slug: "api/effects", title: "Effects", description: "Fade, flash, shake and pulse entity properties with shared easing.", source: "docs/Scripting/API/effects.md" },
{ slug: "api/audio", title: "Audio", description: "List and select Windows audio devices, load reusable audio in Lua/Luau, and play and fade WAV, MP3, AAC, FLAC, and OGG voices.", source: "docs/Scripting/API/audio.md" },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove the duplicate Audio catalog entry

Adding a second entry with the same api/audio slug causes docGroups to expand the Audio category and all of its child calls twice. The published sidebar therefore renders duplicate Audio trees with duplicate React keys, the search index contains duplicate results, and pagination/static parameters repeat the same routes; update the existing entry's description instead of inserting another entry.

AGENTS.md reference: AGENTS.md:L3-L4

Useful? React with 👍 / 👎.

{ slug: "api/audio", title: "Audio", description: "Record audio and read microphone PCM in Lua/Luau; play WAV, MP3, AAC, FLAC, and OGG voices and control fades.", source: "docs/Scripting/API/audio.md" },
{ slug: "api/events", title: "Events and signals", description: "Communicate through queued global events and object-scoped signals across scripting languages.", source: "docs/Scripting/API/events.md" },
{ slug: "api/clock", title: "Clock", description: "Scale or pause the shared scene clock for actions, animation, physics and audio.", source: "docs/Scripting/API/clock.md" },
Expand Down
Loading