Kira Audio Library

repository·main·Indexed 21 days ago

https://github.com/tesselode/kira

A backend-agnostic Rust audio library for games featuring tweens for smooth property transitions, a mixer system with tracks and effects (including compressors, reverb, and filters), a clock system for precise musical timing, and spatial audio support. Designed primarily for desktop platforms with limited support for WASM.

Tokens
13.5K
Snippets
41
Records
56
Agent score
72%

What's inside Kira

  1. Overview of Kira

    main

    Kira is a backend-agnostic Rust library designed for creating expressive audio in games. It provides several core capabilities:

    • Tweens: Smoothly adjust audio properties over time.
    • Mixer: A flexible system for applying effects to audio via tracks.
    • Clock System: Precise timing for audio events based on musical beats or arbitrary ticks.
    • Spatial Audio: Support for positioning sounds in a 3D space.

    To use Kira, you typically initialize an AudioManager with a specific backend (such as DefaultBackend).

  2. Platform support and WASM limitations

    main

    Kira is primarily designed for desktop platforms (Windows, Mac, Linux).

    WASM Support Limitations:

    • Static sounds: Cannot be loaded from files.
    • Streaming sounds: Not supported (due to heavy reliance on threads).
  3. Define audio regions with Region and EndPosition

    main

    A Region represents a specific portion of audio defined by a start and an end.

    EndPosition

    To define where a region ends, use the EndPosition enum:

    • EndOfAudio: The default; the region lasts until the end of the audio data.
    • Custom(PlaybackPosition): A user-defined time/position.

    Creating Regions

    Region implements From for several standard Rust range types, making it easy to define segments using PlaybackPosition values:

    • From<RangeFrom<T>>: Creates a region from a start point to the end of the audio.
    • From<Range<T>>: Creates a region between a specific start and end point.
    • From<RangeTo<T>>: Creates a region from the beginning of the audio to a specific end point.
    • From<RangeFull>: Creates a region covering the entire audio file.
    use kira::sound::{Region, EndPosition};
    // Assuming PlaybackPosition is available via imports
    
    // Create a region from 1.0s to 5.0s
    let region = Region::from(1.0..5.0);
    
    // Create a region from 2.0s to the end
    let region_to_end = Region::from(2.0..);
    
    // Create a region from the start to 3.0s
    let region_from_start = Region::from(..3.0);
  4. Manage the primary audio track with MainTrack

    main

    The MainTrack is the central component for managing the primary audio stream in Kira. It handles the mixing of multiple Sound objects, the application of Effect chains, and global volume control using Decibels.

    Key responsibilities include:

    • Sound Mixing: Aggregating multiple sounds into a single output buffer.
    • Effect Processing: Applying a sequence of effects to the mixed audio.
    • Volume Control: Managing volume changes over time using parameter interpolation.
    • Lifecycle Management: Coordinating sample rate changes and processing start signals across all internal components.
  5. How SoundData and Sound work together

    main

    Kira uses a two-stage process for audio playback involving the SoundData and Sound traits:

    1. SoundData: Represents audio that is loaded into memory or prepared for streaming but is not yet playing. You pass a SoundData implementation to AudioManager::play.
    2. Sound: Represents an actively playing audio source. When you call into_sound() on a SoundData object, it returns a Box<dyn Sound> (which is sent to the audio renderer) and a Handle (which you use to control the sound).

    Built-in Implementations

    Kira provides two primary implementations of SoundData:

    • StaticSoundData: Loads the entire audio chunk into memory. Use this for short sounds, sounds that need to play frequently, or sounds where precise start times are critical.
    • StreamingSoundData: Streams audio from a file or cursor. This is intended for long sounds like background music to save memory. Note: Streaming is only available on desktop platforms.

    If these do not meet your needs, you can implement the SoundData and Sound traits yourself.

    // Example conceptual flow:
    // 1. Load data (StaticSoundData or StreamingSoundData)
    // 2. Play via AudioManager
    // let handle = audio_manager.play(static_sound_data)?;
    // 3. Use handle to control playback
  6. When to use the Parameter type

    main

    The Parameter<T> type manages and updates values that can be smoothly transitioned (tweened) and linked to modulators.

    Important: Most users should not use Parameter directly. If you want to adjust audio properties like volume or clock speed from your gameplay code, use the methods provided on the object's handle (e.g., a SoundHandle or EffectHandle).

    You should only use Parameter if you are implementing your own custom Sound, Effect, or Modulator.

  7. Understand PlaybackState and advancing audio

    main

    The PlaybackState enum represents the current lifecycle stage of a sound. You can use the is_advancing() method to determine if a sound is currently outputting audio and moving forward in time.

    Stateis_advancing()Description
    PlayingtrueNormal playback
    PausingtrueFading out before pausing
    PausedfalsePlayback is stopped temporarily
    WaitingToResumefalsePaused, but scheduled to resume
    ResumingtrueFading back in after a pause
    StoppingtrueFading out before stopping
    StoppedfalsePlayback has ended and cannot be resumed
    use kira::sound::PlaybackState;
    
    let state = PlaybackState::Playing;
    assert!(state.is_advancing());
    
    let paused = PlaybackState::Paused;
    assert!(!paused.is_advancing());
  8. Optimize Kira performance in dev profile

    main

    By default, Rust dev profiles are unoptimized, which can cause poor audio performance and slow loading. To improve this without losing debug benefits, add the following to your Cargo.toml to optimize Kira and its dependencies at level 3:

    [profile.dev.package.kira]
    opt-level = 3
    
    [profile.dev.package.cpal]
    opt-level = 3
    
    [profile.dev.package.symphonia]
    opt-level = 3
    
    # ... and so on for specific symphonia bundles/codecs

    Alternatively, you can optimize all dependencies at once:

    [profile.dev.package.""]
    opt-level = 3
  9. Get started with Kira

    main

    Kira is a backend-agnostic audio library for games. To begin using it, you must create an AudioManager and use it to play either StaticSoundData or StreamingSoundData. The AudioManager manages resources and handles the actual playback of sounds.

    To use the default backend, use AudioManager::<DefaultBackend>::new(AudioManagerSettings::default())?.

    use kira::{AudioManager, AudioManagerSettings, DefaultBackend, sound::static_sound::StaticSoundData};
    
    // Create an audio manager.
    let mut manager = AudioManager::<DefaultBackend>::new(AudioManagerSettings::default())?;
    let sound_data = StaticSoundData::from_file("sound.ogg")?;
    
    // Play the sound
    manager.play(sound_data.clone())?;
  10. Apply audio effects using a Mixer and Tracks

    main

    You can create sub-tracks using a TrackBuilder and attach effects (like a FilterBuilder) to them. When playing a sound, you can route its output to a specific track using .output_destination(&track).

    use kira::{
    	AudioManager, AudioManagerSettings, DefaultBackend,
    	sound::static_sound::StaticSoundData,
    	track::{
    		TrackBuilder,
    		effect::filter::FilterBuilder,
    	},
    };
    
    let mut manager = AudioManager::<DefaultBackend>::new(AudioManagerSettings::default())?;
    // Create a mixer sub-track with a filter.
    let 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")?.output_destination(&track);
    manager.play(sound_data)?;
  11. Play sounds multiple times simultaneously

    main

    You can play the same StaticSoundData multiple times without incurring extra memory costs by cloning the data. Each call to manager.play() returns a handle to the playing sound instance.

    use kira::{
    	AudioManager, AudioManagerSettings, DefaultBackend,
    	sound::static_sound::StaticSoundData,
    };
    
    // 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.
  12. Schedule sounds using the Clock system

    main

    Kira's clock system allows you to time audio events to musical beats or other arbitrary intervals. You can create a clock with a specific ClockSpeed (e.g., TicksPerMinute) and schedule sounds to start at a specific future time relative to the clock's current time using .start_time(clock.time() + ticks).

    use kira::{
    	AudioManager, AudioManagerSettings, DefaultBackend,
    	sound::static_sound::StaticSoundData,
    	clock::ClockSpeed,
    };
    
    const TEMPO: f64 = 120.0;
    
    let mut manager = AudioManager::<DefaultBackend>::new(AudioManagerSettings::default())?;
    // Create a clock that ticks 120 times per minute. 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()?;