I'm seeing some unexpected FM behavior in both Rodio 0.20.1 and 0.21.0
Below is my main.rs demonstrating the problem. When DEMONSTRATING_DEFECT is false, you hear what you would expect, a siren-like sound alternating between two frequencies.
When DEMONSTRATING_DEFECT is true, you would expect smooth alternation between one frequency and the next. Instead, the high frequency gets higher and the low frequency seems to get lower...
use std::{f32::consts::PI, io::stdin};
use rodio::{OutputStream, OutputStreamBuilder, Source};
const SAMPLE_RATE: u32 = 44_100;
struct FrequencyModulation {
sample_num: u32,
frequency: Box<dyn Fn(f32) -> f32 + Send + 'static>,
}
fn amplitude(time_in_secs: f32, hz: f32) -> f32 /* (-1.0, 1.0) */ {
(2.0 * PI * hz * time_in_secs).sin()
}
fn time(sample_num: u32) -> f32 {
sample_num as f32 / SAMPLE_RATE as f32
}
impl FrequencyModulation {
pub fn new(frequency: Box<dyn Fn(f32) -> f32 + Send + 'static>) -> FrequencyModulation {
FrequencyModulation { sample_num: 0, frequency }
}
}
impl Source for FrequencyModulation {
fn current_span_len(&self) -> Option<usize> { None }
fn channels(&self) -> u16 { 1 }
fn sample_rate(&self) -> u32 { SAMPLE_RATE }
fn total_duration(&self) -> Option<std::time::Duration> { None }
}
impl Iterator for FrequencyModulation {
type Item = f32;
fn next(&mut self) -> Option<Self::Item> {
let time_in_secs = time(self.sample_num);
let instantaneous_frequency = (self.frequency)(time_in_secs);
let sample = amplitude(time_in_secs, instantaneous_frequency);
self.sample_num = self.sample_num + 1;
Some(sample)
}
}
const DEMONSTRATING_DEFECT: bool = true;
fn main() {
let stream_handle = OutputStreamBuilder::open_default_stream().unwrap();
let freq = if DEMONSTRATING_DEFECT {
// behaves unexpectedly with high rising and low lowering
|t: f32| 440.0 + 50.0 * (t * 2.0 * PI).sin()
} else {
// behaves as expected with siren-like alternating pitch
|t: f32| 440.0 + if (t * 2.0 * PI).sin() > 0.0 { 50.0 } else { -50.0 }
};
let source = FrequencyModulation::new(Box::new(freq));
let _ = stream_handle.mixer().add(source);
stdin().read_line(&mut String::new()).unwrap();
}