phonic is a cross-platform audio playback and DSP library for Rust. It provides a flexible, low-latency
audio engine and related tools for desktop and web-based music applications
Originally developed for the AFEC-Explorer app, phonic initially addressed the need for precise playback position monitoring not found in other Rust audio libraries. It is now also used as the default sample playback engine for the experimental algorithmic sequencer pattrns.
[!NOTE]
phonic has not yet reached a stable version, so expect breaking changes.
Features
-
Cross-Platform Audio Playback:
- Play audio on Windows, macOS, and Linux via cpal.
- WebAssembly support for in-browser audio via emscripten.
- WAV file output for rendering computed audio to a file instead of playing it back.
-
Flexible Audio Source Handling:
- Play, seek, stop, and mix preloaded (buffered) or streamed (on-the-fly decoded) audio files.
- Support for most common audio formats through Symphonia.
- Seamless loop playback using loop points from WAV and FLAC files.
- Automatic resampling and channel mapping via a fast custom resampler and Rubato.
-
Advanced Playback Control:
- Sample-precise scheduling for accurate sequencing.
- Real-time monitoring of playback position and status for GUI integration.
- Dynamic control over volume, panning, and playback speed.
-
Custom Synthesis and DSPs:
- Build simple or complex DSP graphs by routing audio through optional sub-mixers.
- Play completely custom-built synthesizers or use the optional dasp integration for creating synth sources.
- Apply custom-built DSP effects or use built-in effects: gain, filter, eq, reverb, chorus, compressor, limiter, distortion.
Documentation
Rust docs for the last published versions are available at https://docs.rs/phonic
Examples
See /examples directory for more examples.
File Playback with Monitoring
Play, seek and stop audio files on the default audio output device.
Monitor playback status of playing files.
use phonic::{
DefaultOutputDevice, Player, PlaybackStatusEvent, Error,
FilePlaybackOptions, SynthPlaybackOptions
};
fn main() -> Result<(), Error> {
let (playback_status_sender, playback_status_receiver) = crossbeam_channel::bounded(32);
let mut player = Player::new(DefaultOutputDevice::open()?, playback_status_sender);
let small_file_id = player.play_file(
"PATH_TO/some_small_file.wav",
FilePlaybackOptions::default())?;
let long_file_id = player.play_file(
"PATH_TO/some_long_file.mp3",
FilePlaybackOptions::default()
.streamed()
.volume_db(-6.0)
.speed(0.5)
.repeat(2),
)?;
std::thread::spawn(move || {
while let Ok(event) = playback_status_receiver.recv() {
match event {
PlaybackStatusEvent::Position { id, path, context: _, position } => {
println!("Playback pos of source #{id} '{path}': {pos}",
pos = position.as_secs_f32()
);
}
PlaybackStatusEvent::Stopped { id, path, context: _, exhausted, } => {
if exhausted {
println!("Playback of #{id} '{path}' finished");
} else {
println!("Playback of #{id} '{path}' was stopped");
}
}
}
}
});
player.seek_source(long_file_id, std::time::Duration::from_secs(5), None)?;
player.stop_source(small_file_id, None)?;
player.stop_all_sources()?;
player.play_file("PATH_TO/boom.wav", FilePlaybackOptions::default())?;
Ok(())
}
File playback with DSP Effects in a Mixer Graph
Create complex audio processing chains by routing sources through different mixers and effects.
use phonic::{
DefaultOutputDevice, Player, Error, FilePlaybackOptions,
effects::{ChorusEffect, ReverbEffect}
};
fn main() -> Result<(), Error> {
let mut player = Player::new(DefaultOutputDevice::open()?, None);
player.add_effect(ReverbEffect::with_parameters(0.6, 0.8), None)?;
let chorus_mixer_id = player.add_mixer(None)?;
player.add_effect(ChorusEffect::default(), chorus_mixer_id)?;
player.play_file(
"PATH_TO/some_file.wav",
FilePlaybackOptions::default(),
)?;
player.play_file(
"PATH_TO/another_file.wav",
FilePlaybackOptions::default().target_mixer(chorus_mixer_id),
)?;
Ok(())
}
Contributing
Patches are welcome! Please fork the latest git repository and create a feature or bugfix branch.
License
phonic is distributed under the terms of the GNU Affero General Public License V3.