use tunes::prelude::*;
fn main() -> anyhow::Result<()> {
println!("\n🎵 Sample Playback Demo\n");
println!("Creating test sample (440Hz tone)...");
create_test_sample("test_tone.wav", 440.0, 0.5)?;
println!(" ✓ Created test_tone.wav\n");
let mut comp = Composition::new(Tempo::new(120.0));
println!("Loading sample into composition...");
comp.load_sample("tone", "test_tone.wav")?;
println!(" ✓ Loaded 'tone' sample\n");
println!("Example 1: Basic Playback");
comp.track("basic")
.at(0.0)
.sample("tone") .sample("tone") .sample("tone"); println!(" ✓ Playing sample 3 times\n");
println!("Example 2: Pitch Shifting");
comp.track("pitched")
.at(2.0)
.sample_with_rate("tone", 1.0) .sample_with_rate("tone", 1.5) .sample_with_rate("tone", 2.0) .sample_with_rate("tone", 0.5); println!(" ✓ Playing with different pitch shifts\n");
println!("Example 3: Rhythmic Pattern");
comp.track("rhythm")
.at(5.0)
.sample_with_rate("tone", 1.0)
.sample_with_rate("tone", 1.25)
.sample_with_rate("tone", 1.5)
.sample_with_rate("tone", 2.0)
.sample_with_rate("tone", 1.5)
.sample_with_rate("tone", 1.25)
.sample_with_rate("tone", 1.0)
.sample_with_rate("tone", 0.75);
println!(" ✓ Playing melodic pattern with samples\n");
println!("=== Playback ===");
let engine = AudioEngine::new()?;
let mixer = comp.into_mixer();
println!(
"Playing {:.1}s composition with samples...\n",
mixer.total_duration()
);
engine.play_mixer(&mixer)?;
println!("\n=== Convenience Method Demo ===\n");
println!("🎮 NEW: play_sample() - Fire-and-forget sound effects!");
println!("\nBefore play_sample() existed:");
println!(" let mut comp = Composition::new(Tempo::new(120.0));");
println!(" comp.track(\"sfx\").sample(\"boom.wav\");");
println!(" engine.play_mixer_realtime(&comp.into_mixer())?;");
println!("\nNow you can simply write:");
println!(" engine.play_sample(\"boom.wav\")?;");
println!("\nPerfect for game sound effects - non-blocking and concurrent!\n");
create_test_sample("sfx_beep.wav", 880.0, 0.1)?;
create_test_sample("sfx_boop.wav", 440.0, 0.1)?;
println!("Example 4: Fire-and-forget SFX");
std::thread::sleep(std::time::Duration::from_millis(500));
println!(" [Player jumps]");
engine.play_sample("sfx_beep.wav"); std::thread::sleep(std::time::Duration::from_millis(200));
println!(" [Collects coin]");
engine.play_sample("sfx_boop.wav");
std::thread::sleep(std::time::Duration::from_millis(150));
println!(" [Another coin]");
engine.play_sample("sfx_boop.wav");
std::thread::sleep(std::time::Duration::from_millis(150));
println!(" [Three beeps simultaneously!]");
engine.play_sample("sfx_beep.wav");
engine.play_sample("sfx_beep.wav");
engine.play_sample("sfx_beep.wav");
std::thread::sleep(std::time::Duration::from_millis(500));
println!("\n You can still control sounds if needed:");
engine.play_sample("sfx_boop.wav").volume(0.3);
println!(" Playing at 30% volume...");
std::thread::sleep(std::time::Duration::from_secs(1));
println!("\n✅ Demo complete!");
println!("\nKey features demonstrated:");
println!(" • Loading WAV files as samples");
println!(" • Basic sample playback");
println!(" • Pitch shifting via playback_rate");
println!(" • Creating rhythmic/melodic patterns with samples");
println!(" • Fire-and-forget sound effects with play_sample()");
println!(" • Concurrent sound playback (no blocking!)");
println!("\nYou can now use your own drum samples, vocals, or any WAV files!");
std::fs::remove_file("test_tone.wav").ok();
std::fs::remove_file("sfx_beep.wav").ok();
std::fs::remove_file("sfx_boop.wav").ok();
Ok(())
}
fn create_test_sample(path: &str, frequency: f32, duration: f32) -> anyhow::Result<()> {
let spec = hound::WavSpec {
channels: 1,
sample_rate: 44100,
bits_per_sample: 16,
sample_format: hound::SampleFormat::Int,
};
let mut writer = hound::WavWriter::create(path, spec)?;
let sample_rate = 44100.0;
let num_samples = (duration * sample_rate) as usize;
for i in 0..num_samples {
let t = i as f32 / sample_rate;
let sample = (t * frequency * 2.0 * std::f32::consts::PI).sin();
let envelope = if t < 0.01 {
t / 0.01 } else if t > duration - 0.01 {
(duration - t) / 0.01 } else {
1.0
};
let amplitude = (sample * envelope * 32767.0) as i16;
writer.write_sample(amplitude)?;
}
writer.finalize()?;
Ok(())
}