phonic is an audio playback library for Rust, based on the
psst-core audio playback
implementation.
It serves as a general-purpose, low-latency audio playback engine for Rust-based music applications while also being suitable as an audio backend for game engines.
Originally developed for the AFEC-Explorer app using the Tauri framework, phonic addressed the need for precise playback position monitoring not found in other Rust audio libraries. It is also used as the default sample playback engine for the experimental algorithmic sequencer afseq.
Features
- Play, seek, stop, mix and monitor playback of preloaded (buffered) or streamed
(on-the-fly decoded) audio files.
- Play, stop, mix and monitor playback of custom synth tones thanks to
dasp (optional feature: disabled by default).
- Play audio on Windows, macOS, Linux or the Web via cpal or
sokol-audio (cpal is enabled by default).
- Decodes and thus plays back most common audio file formats, thanks to
Symphonia.
- Files are automatically resampled and channel mapped using a fast custom resampler or rubato.
- Runs on the web via sokol thanks to emscripten: see play-emscripten example.
- Click free playback: when stopping sounds, a very short volume fade-out is applied to
avoid clicks.
- Sample precise playback scheduling, e.g. to play back sounds in a sequencer.
- Monitor playback positions and status of all played back files for GUIs.
Examples
See /examples directory for more examples.
Simple Playback
Play and stop audio files on the system's default audio output device.
use phonic::{
Player, OutputDevice, OutputSink, DefaultOutputDevice, Error, FilePlaybackOptions
};
fn main() -> Result<(), Error> {
let device = DefaultOutputDevice::open()?;
let mut player = Player::new(device.sink(), None);
player.play_file(
"PATH_TO/some_file.wav",
FilePlaybackOptions::default())?;
player.play_file(
"PATH_TO/some_long_file.mp3",
FilePlaybackOptions::default()
.streamed() .volume_db(-6.0) .speed(0.5) .repeat(2), )?;
player.stop_all_sources()?;
Ok(())
}
Advanced Playback
Play, seek and stop audio files and synth sounds on the default audio output device.
Monitor playback status of playing files and synth tones.
use phonic::{
Player, OutputDevice, OutputSink, PlaybackStatusEvent,
DefaultOutputDevice, Error, FilePlaybackOptions, SynthPlaybackOptions
};
fn main() -> Result<(), Error> {
let device = DefaultOutputDevice::open()?;
let (playback_status_sender, playback_status_receiver) = crossbeam_channel::bounded(32);
let mut player = Player::new(device.sink(), Some(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,
position.as_secs_f32()
);
}
PlaybackStatusEvent::Stopped {
id,
path,
context: _,
exhausted,
} => {
if exhausted {
println!("Playback of #{} '{}' finished", id, path);
} else {
println!("Playback of #{} '{}' was stopped", id, path);
}
}
}
}
});
player.seek_source(long_file_id, std::time::Duration::from_secs(5))?;
player.stop_source(small_file_id)?;
player.stop_all_sources()?;
player.play_file("PATH_TO/boom.wav", FilePlaybackOptions::default())?;
Ok(())
}
Playback Sequencing
Play a sample file sequence in time with e.g. musical beats.
use phonic::{
Player, OutputDevice, DefaultOutputDevice, Error, FilePlaybackOptions,
utils::speed_from_note, PreloadedFileSource
};
fn main() -> Result<(), Error> {
let mut player = Player::new(DefaultOutputDevice::open()?.sink(), None);
let beats_per_min = 120.0;
let samples_per_sec = player.output_sample_rate();
let samples_per_beat = samples_per_sec as f64 * 60.0 / beats_per_min as f64;
let sample = PreloadedFileSource::new(
"path/to_some_file.wav",
None, FilePlaybackOptions::default(),
samples_per_sec,
)?;
let playback_start = player.output_sample_frame_position() as f64;
for beat_counter in 0..8 {
let play_time = playback_start + (beat_counter as f64 * samples_per_beat);
player.play_file_source(
sample.clone(
FilePlaybackOptions::default()
.speed(speed_from_note(60)), samples_per_sec)?,
Some(play_time as u64),
)?;
}
Ok(())
}
License
phonic is distributed under the terms of both the MIT license and the Apache License (Version 2.0).