Files
benilla-octo/third_party/kira/src/lib.rs
T
gromandClaude Opus 5 81d96af35d Everwood: the HD/OctoWoW line of benilla, imported onto upstream
This checkout had no git history at all, so this one commit carries the whole
of the Everwood work as a single import on top of chairmanpow/benilla-octo's
`main` (706cd71). The two trees have genuinely diverged — each has files the
other does not — so this is a fork point, not a catch-up.

Today's work, which is the reason a baseline exists at all:

- Booth previews no longer cast world shadows (`NO_BOOTH_SHADOW`). Portrait
  characters were entering the sun's shadow cascades, so every skinned-rig
  shadow specialization compiled live mid-frame; the hitch detector logged
  "main thread blocked 1316 ms" beside a `compiled LIVE` warning. This was
  the Fashion/Tmog stutter.
- Per-pane dress-up: each `DressUpModel` pane assembles and stages its own
  tried-on look instead of every pane mirroring the live player.
- Item template cache is append-only JSONL written on the IO pool, replacing
  an 11.6 MB whole-map JSON rewrite on the main thread every 10 s.
- `WOW_SYSTEM_TIMING` rebuilt per-thread: it took 2 mutexes per system enter
  and 4 per exit, costing 15 ms a frame (590 fps -> 60) and corrupting the
  measurements it was taken to produce.
- The visibility-churn sweep moved to its own `WOW_FLASH_CHURN` switch, so a
  frame-cost reading no longer pays 2.7 ms a frame for it.

Local state (`benilla-config/`, `logs/`, `target/`) is excluded by the
existing .gitignore; the account credential it holds is not in this commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
2026-10-15 15:01:14 -06:00

257 lines
7.9 KiB
Rust

/*!
# Kira
Kira is a backend-agnostic library to create expressive audio for games. It provides
tweens for smoothly adjusting properties of sounds, a flexible [mixer](track)
for applying [effects](effect) to audio, a [clock] system for precisely timing audio events,
and [spatial audio](crate::track#spatial-tracks) support.
To get started, create an [`AudioManager`] and use it to [play](AudioManager::play) a
[`StaticSoundData`](crate::sound::static_sound::StaticSoundData) or
[`StreamingSoundData`](crate::sound::streaming::StreamingSoundData).
## Examples
Playing a sound multiple times simultaneously:
```rust, no_run
# extern crate kira;
#
use kira::{
AudioManager, AudioManagerSettings, DefaultBackend,
sound::static_sound::{StaticSoundData, StaticSoundSettings},
};
// Create an audio manager. This plays sounds and manages resources.
let mut manager = AudioManager::<DefaultBackend>::new(AudioManagerSettings::default())?;
let sound_data = StaticSoundData::from_file("sound.ogg")?;
manager.play(sound_data.clone())?;
// After a couple seconds...
manager.play(sound_data.clone())?;
// Cloning the sound data will not use any extra memory.
# Result::<(), Box<dyn std::error::Error>>::Ok(())
```
Gradually speeding up a sound over time:
```rust, no_run
# extern crate kira;
#
use std::time::Duration;
use kira::{
AudioManager, AudioManagerSettings, DefaultBackend,
sound::static_sound::{StaticSoundData, StaticSoundSettings},
Tween,
};
let mut manager = AudioManager::<DefaultBackend>::new(AudioManagerSettings::default())?;
let sound_data = StaticSoundData::from_file("sound.ogg")?;
let mut sound = manager.play(sound_data)?;
// Start smoothly adjusting the playback rate parameter.
sound.set_playback_rate(
2.0,
Tween {
duration: Duration::from_secs(3),
..Default::default()
},
);
# Result::<(), Box<dyn std::error::Error>>::Ok(())
```
Playing a sound with a low-pass filter applied (this makes the
audio sound muffled):
```rust, no_run
# extern crate kira;
#
use kira::{
AudioManager, AudioManagerSettings, DefaultBackend,
sound::static_sound::{StaticSoundData, StaticSoundSettings},
track::TrackBuilder,
effect::filter::FilterBuilder,
};
let mut manager = AudioManager::<DefaultBackend>::new(AudioManagerSettings::default())?;
// Create a mixer sub-track with a filter.
let mut track = manager.add_sub_track({
let mut builder = TrackBuilder::new();
builder.add_effect(FilterBuilder::new().cutoff(1000.0));
builder
})?;
// Play the sound on the track.
let sound_data = StaticSoundData::from_file("sound.ogg")?;
track.play(sound_data)?;
# Result::<(), Box<dyn std::error::Error>>::Ok(())
```
Playing sounds in time with a musical beat:
```rust, no_run
# extern crate kira;
#
use kira::{
AudioManager, AudioManagerSettings, DefaultBackend,
sound::static_sound::{StaticSoundData, StaticSoundSettings},
clock::ClockSpeed,
};
const TEMPO: f64 = 120.0;
let mut manager = AudioManager::<DefaultBackend>::new(AudioManagerSettings::default())?;
// Create a clock that ticks 120 times per second. In this case,
// each tick is one musical beat. We can use a tick to represent any
// arbitrary amount of time.
let mut clock = manager.add_clock(ClockSpeed::TicksPerMinute(TEMPO))?;
// Play a sound 2 ticks (beats) from now.
let sound_data_1 = StaticSoundData::from_file("sound1.ogg")?
.start_time(clock.time() + 2);
manager.play(sound_data_1)?;
// Play a different sound 4 ticks (beats) from now.
let sound_data_2 = StaticSoundData::from_file("sound2.ogg")?
.start_time(clock.time() + 4);
manager.play(sound_data_2)?;
// Start the clock.
clock.start();
# Result::<(), Box<dyn std::error::Error>>::Ok(())
```
## Features
The Kira crate has the following feature flags:
- `cpal` (enabled by default) - enables the cpal backend and makes it the default for
audio managers. This allows Kira to talk to the operating system to output audio. Most
users should leave this enabled.
- `symphonia` (enabled by default) - allows loading and streaming audio from common
audio formats, like MP3 and WAV.
- Audio formats: `aiff`, `caf`, `isomp4`, `mkv`, `ogg`, `wav` - these correspond to the
Symphonia features of the same name
- Audio codecs: `aac`, `adpcm`, `alac`, `he-aac`, `he-aac-v2`, `flac`, `mp1`, `mp2`, `mp3`,
`mpa`, `opus', `pcm`, `vorbis`, `wavpack` - these correspond to the Symphonia features
of the same name
- `mp3`, `ogg`, `vorbis`, `flac`, `wav`, and `pcm` are enabled by default.
- [See Symphonia's documentation for more information about these flags.](https://github.com/pdeljanov/Symphonia?tab=readme-ov-file#formats-demuxers)
- `serde` - adds `Serialize` and `Deserialize` implementations for the following types:
- [`Capacities`]
- [`ClockSpeed`](crate::clock::ClockSpeed)
- [`DistortionKind`](crate::effect::distortion::DistortionKind)
- [`Easing`]
- [`EndPosition`](crate::sound::EndPosition)
- [`EqFilterKind`](crate::effect::eq_filter::EqFilterKind)
- [`FilterMode`](crate::effect::filter::FilterMode)
- [`Frame`]
- [`Mapping`]
- [`PlaybackPosition`](crate::sound::PlaybackPosition)
- [`PlaybackRate`]
- [`PlaybackState`](crate::sound::PlaybackState)
- [`Region`](crate::sound::Region)
- [`Decibels`]
- [`Waveform`](crate::modulator::lfo::Waveform)
- `assert_no_alloc` - uses the [`assert_no_alloc`](https://crates.io/crates/assert_no_alloc) crate
to cause panics if memory is allocated or deallocated on the audio thread. This is mainly useful
for people developing Kira itself.
## Performance using the `dev` profile
By default, Rust programs run with the `dev` profile are not optimized. This can
lead to poor performance of audio playback and long loading times for audio
files. You can alleviate this by building Kira and its audio-related
dependencies with a higher optimization level. Add the following to your
Cargo.toml:
```toml
[profile.dev.package.kira]
opt-level = 3
[profile.dev.package.cpal]
opt-level = 3
[profile.dev.package.symphonia]
opt-level = 3
[profile.dev.package.symphonia-bundle-mp3]
opt-level = 3
[profile.dev.package.symphonia-format-ogg]
opt-level = 3
[profile.dev.package.symphonia-codec-vorbis]
opt-level = 3
[profile.dev.package.symphonia-bundle-flac]
opt-level = 3
[profile.dev.package.symphonia-format-wav]
opt-level = 3
[profile.dev.package.symphonia-codec-pcm]
opt-level = 3
```
You can also build all of your projects with a higher optimization level by
using this snippet instead:
```toml
[profile.dev.package."*"]
opt-level = 3
```
Building dependencies with a higher optimization level does increase compile
times, but only when compiling your project from scratch. If you only make
changes to your crate, you're not recompiling the dependencies, so you don't
suffer from the longer compilation step in that case. Building dependencies
optimized and the main crate unoptimized can be a good balance of performance
and compile times for games.
*/
#![cfg_attr(docsrs, feature(doc_cfg))]
#![allow(clippy::collapsible_if)]
#![allow(clippy::collapsible_else_if)]
#![allow(clippy::doc_lazy_continuation)]
#![allow(clippy::large_enum_variant)]
#![allow(clippy::enum_variant_names)]
#![warn(clippy::todo)]
#![warn(missing_docs)]
#![allow(clippy::tabs_in_doc_comments)]
pub mod backend;
pub mod clock;
pub mod command;
mod decibels;
pub mod effect;
mod error;
mod frame;
pub mod info;
pub mod listener;
mod manager;
mod mix;
pub mod modulator;
mod panning;
mod parameter;
mod playback_rate;
mod playback_state_manager;
mod semitones;
pub mod sound;
mod start_time;
#[cfg(test)]
mod test_helpers;
pub mod track;
mod tween;
mod value;
pub use backend::DefaultBackend;
pub use decibels::*;
pub use error::*;
pub use frame::*;
pub use manager::*;
pub use mix::*;
pub use panning::*;
pub use parameter::*;
pub use pastey;
pub use playback_rate::*;
pub use semitones::*;
pub use start_time::*;
pub use tween::*;
pub use value::*;