Skip to content

Commit a4df8a1

Browse files
committed
configurable sample rate for fyrox-sound
1 parent 1c18ee7 commit a4df8a1

15 files changed

Lines changed: 156 additions & 116 deletions

File tree

fyrox-impl/src/engine/mod.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,6 @@ pub struct Engine {
315315

316316
model_events_receiver: Receiver<ResourceEvent>,
317317

318-
#[allow(dead_code)] // Keep engine instance alive.
319318
sound_engine: SoundEngine,
320319

321320
// A set of plugins used by the engine.
@@ -1501,6 +1500,17 @@ impl Engine {
15011500
}
15021501
}
15031502

1503+
/// Returns current sample rate of the sound engine.
1504+
pub fn sound_sample_rate(&self) -> u32 {
1505+
self.sound_engine.sample_rate()
1506+
}
1507+
1508+
/// Normalizes given frequency using sampling rate of the sound output device. Normalized frequency
1509+
/// then can be used to create filters.
1510+
pub fn normalize_sound_frequency(&self, f: f32) -> f32 {
1511+
self.sound_engine.normalize_frequency(f)
1512+
}
1513+
15041514
/// Tries to destroy current graphics context. It will succeed only if the `graphics_context` is fully initialized.
15051515
/// The method will try to save all possible runtime changes of the window, so the next [`Engine::initialize_graphics_context`]
15061516
/// will result in the almost exact copy of the context that was made before destruction.

fyrox-impl/src/scene/sound/context.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,6 @@ impl SoundContextGuard<'_> {
8080
self.guard.distance_model()
8181
}
8282

83-
/// Normalizes given frequency using context's sampling rate. Normalized frequency then can be used
84-
/// to create filters.
85-
pub fn normalize_frequency(&self, f: f32) -> f32 {
86-
self.guard.normalize_frequency(f)
87-
}
88-
8983
/// Returns amount of time context spent on rendering all sound sources.
9084
pub fn full_render_duration(&self) -> Duration {
9185
self.guard.full_render_duration()

fyrox-impl/src/scene/sound/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub use fyrox_sound::{
4747
DataSource, SoundBuffer, SoundBufferResource, SoundBufferResourceLoadError,
4848
},
4949
bus::*,
50-
context::{DistanceModel, SAMPLE_RATE},
50+
context::DistanceModel,
5151
dsp::{filters::*, DelayLine},
5252
effects::*,
5353
engine::SoundEngine,

fyrox-sound/examples/hrtf.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use fyrox_sound::renderer::hrtf::{HrirSphereResource, HrirSphereResourceExt};
2626
use fyrox_sound::{
2727
algebra::{UnitQuaternion, Vector3},
2828
buffer::{DataSource, SoundBufferResource},
29-
context::{self, SoundContext},
29+
context::SoundContext,
3030
engine::SoundEngine,
3131
futures::executor::block_on,
3232
hrtf::HrirSphere,
@@ -44,7 +44,7 @@ fn main() {
4444
let engine = SoundEngine::new().unwrap();
4545

4646
let hrir_path = PathBuf::from("examples/data/IRC_1002_C.bin");
47-
let hrir_sphere = HrirSphere::from_file(&hrir_path, context::SAMPLE_RATE).unwrap();
47+
let hrir_sphere = HrirSphere::from_file(&hrir_path, engine.sample_rate()).unwrap();
4848

4949
// Initialize new sound context with default output device.
5050
let context = SoundContext::new();
@@ -55,6 +55,7 @@ fn main() {
5555
context
5656
.state()
5757
.set_renderer(Renderer::HrtfRenderer(HrtfRenderer::new(
58+
engine.sample_rate(),
5859
HrirSphereResource::from_hrir_sphere(hrir_sphere, ResourceKind::External),
5960
)));
6061

fyrox-sound/examples/reverb.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use fyrox_sound::renderer::hrtf::{HrirSphereResource, HrirSphereResourceExt};
2525
use fyrox_sound::{
2626
algebra::{Point3, UnitQuaternion, Vector3},
2727
buffer::{DataSource, SoundBufferResource},
28-
context::{self, SoundContext},
28+
context::SoundContext,
2929
effects::{reverb::Reverb, Effect},
3030
engine::SoundEngine,
3131
futures::executor::block_on,
@@ -40,12 +40,12 @@ use std::{
4040
};
4141

4242
fn main() {
43-
let hrir_path = PathBuf::from("examples/data/IRC_1002_C.bin");
44-
let hrir_sphere = HrirSphere::from_file(&hrir_path, context::SAMPLE_RATE).unwrap();
45-
4643
// Initialize sound engine with default output device.
4744
let engine = SoundEngine::new().unwrap();
4845

46+
let hrir_path = PathBuf::from("examples/data/IRC_1002_C.bin");
47+
let hrir_sphere = HrirSphere::from_file(&hrir_path, engine.sample_rate()).unwrap();
48+
4949
// Initialize new sound context.
5050
let context = SoundContext::new();
5151

@@ -55,6 +55,7 @@ fn main() {
5555
context
5656
.state()
5757
.set_renderer(Renderer::HrtfRenderer(HrtfRenderer::new(
58+
engine.sample_rate(),
5859
HrirSphereResource::from_hrir_sphere(hrir_sphere, ResourceKind::External),
5960
)));
6061

fyrox-sound/examples/write_wav.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ fn main() {
6464
// Create output wav file. The sample rate is currently fixed.
6565
let wav_spec = hound::WavSpec {
6666
channels: 2,
67-
sample_rate: fyrox_sound::context::SAMPLE_RATE,
67+
sample_rate: engine.sample_rate(),
6868
bits_per_sample: 32,
6969
sample_format: hound::SampleFormat::Float,
7070
};
@@ -76,7 +76,7 @@ fn main() {
7676
let mut samples_written = 0;
7777

7878
// Wait until sound will play completely.
79-
while samples_written < 3 * fyrox_sound::context::SAMPLE_RATE {
79+
while samples_written < 3 * engine.sample_rate() {
8080
engine.state().render(&mut buf);
8181
for &(l, r) in buf.iter() {
8282
wav_writer.write_sample(l).unwrap();

fyrox-sound/output.wav

1.02 MB
Binary file not shown.

fyrox-sound/src/bus.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -185,11 +185,11 @@ impl AudioBus {
185185
}
186186
}
187187

188-
fn apply_effects(&mut self) {
188+
fn apply_effects(&mut self, sample_rate: u32) {
189189
// Pass through the chain of effects.
190190
for effect in self.effects.iter_mut() {
191191
let (input, output) = self.ping_pong_buffer.input_output_buffers();
192-
effect.render(input, output);
192+
effect.render(sample_rate, input, output);
193193
self.ping_pong_buffer.swap();
194194
}
195195
}
@@ -487,10 +487,10 @@ impl AudioBusGraph {
487487
}
488488
}
489489

490-
pub(crate) fn end_render(&mut self, output_device_buffer: &mut [(f32, f32)]) {
490+
pub(crate) fn end_render(&mut self, sample_rate: u32, output_device_buffer: &mut [(f32, f32)]) {
491491
let mut leafs = Vec::new();
492492
for (handle, bus) in self.buses.pair_iter_mut() {
493-
bus.apply_effects();
493+
bus.apply_effects(sample_rate);
494494

495495
if bus.child_buses.is_empty() {
496496
leafs.push(handle);
@@ -531,6 +531,8 @@ mod test {
531531
effects::{Attenuate, Effect},
532532
};
533533

534+
const SAMPLE_RATE: u32 = 44100;
535+
534536
#[test]
535537
fn test_multi_bus_data_flow() {
536538
let mut output_buffer = [(0.0f32, 0.0f32)];
@@ -553,7 +555,7 @@ mod test {
553555
*right = 1.0;
554556
}
555557

556-
graph.end_render(&mut output_buffer);
558+
graph.end_render(SAMPLE_RATE, &mut output_buffer);
557559

558560
assert_eq!(output_buffer[0], (2.0, 2.0));
559561
}
@@ -572,7 +574,7 @@ mod test {
572574
*right = 1.0;
573575
}
574576

575-
graph.end_render(&mut output_buffer);
577+
graph.end_render(SAMPLE_RATE, &mut output_buffer);
576578

577579
assert_eq!(output_buffer[0], (1.0, 1.0));
578580
}
@@ -606,7 +608,7 @@ mod test {
606608
*right = 1.0;
607609
}
608610

609-
graph.end_render(&mut output_buffer);
611+
graph.end_render(SAMPLE_RATE, &mut output_buffer);
610612

611613
assert_eq!(output_buffer[0], (0.75, 0.75));
612614
}

fyrox-sound/src/context.rs

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,6 @@ use std::{
5151
};
5252
use strum_macros::{AsRefStr, EnumString, VariantNames};
5353

54-
/// Sample rate for output device.
55-
/// TODO: Make this configurable, for now its set to most commonly used sample rate of 44100 Hz.
56-
pub const SAMPLE_RATE: u32 = 44100;
57-
5854
/// Distance model defines how volume of sound will decay when distance to listener changes.
5955
#[derive(Copy, Clone, Debug, Eq, PartialEq, Reflect, Visit, AsRefStr, EnumString, VariantNames)]
6056
#[repr(u32)]
@@ -186,12 +182,6 @@ impl State {
186182
self.distance_model
187183
}
188184

189-
/// Normalizes given frequency using context's sampling rate. Normalized frequency then can be used
190-
/// to create filters.
191-
pub fn normalize_frequency(&self, f: f32) -> f32 {
192-
f / SAMPLE_RATE as f32
193-
}
194-
195185
/// Returns amount of time context spent on rendering all sound sources.
196186
pub fn full_render_duration(&self) -> Duration {
197187
self.render_duration
@@ -275,7 +265,7 @@ impl State {
275265
&mut self.bus_graph
276266
}
277267

278-
pub(crate) fn render(&mut self, output_device_buffer: &mut [(f32, f32)]) {
268+
pub(crate) fn render(&mut self, sample_rate: u32, output_device_buffer: &mut [(f32, f32)]) {
279269
let last_time = fyrox_core::instant::Instant::now();
280270

281271
if !self.paused {
@@ -294,7 +284,7 @@ impl State {
294284
{
295285
if let Some(bus_input_buffer) = self.bus_graph.try_get_bus_input_buffer(&source.bus)
296286
{
297-
source.render(output_device_buffer.len());
287+
source.render(sample_rate, output_device_buffer.len());
298288

299289
match self.renderer {
300290
Renderer::Default => {
@@ -308,6 +298,7 @@ impl State {
308298
}
309299
Renderer::HrtfRenderer(ref mut hrtf_renderer) => {
310300
hrtf_renderer.render_source(
301+
sample_rate,
311302
source,
312303
&self.listener,
313304
self.distance_model,
@@ -318,7 +309,7 @@ impl State {
318309
}
319310
}
320311

321-
self.bus_graph.end_render(output_device_buffer);
312+
self.bus_graph.end_render(sample_rate, output_device_buffer);
322313
}
323314

324315
self.render_duration = fyrox_core::instant::Instant::now() - last_time;

fyrox-sound/src/effects/filter.rs

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
//! Contains various filter effects. For example, lowpass filter could be used to muffle sounds.
2222
2323
use crate::{
24-
context::SAMPLE_RATE,
2524
dsp::{filters::Biquad, filters::BiquadKind},
2625
effects::EffectRenderTrait,
2726
};
@@ -44,6 +43,10 @@ macro_rules! define_filter_effect {
4443
#[reflect(setter = "set_quality")]
4544
quality: f32,
4645

46+
#[reflect(hidden)]
47+
#[visit(skip)]
48+
sample_rate: u32,
49+
4750
#[reflect(hidden)]
4851
left: Biquad,
4952
#[reflect(hidden)]
@@ -52,20 +55,37 @@ macro_rules! define_filter_effect {
5255

5356
impl Default for $name {
5457
fn default() -> Self {
55-
let mut filter = Self {
58+
Self {
5659
cutoff_frequency_hz: 2200.0,
5760
gain: 1.0,
5861
quality: 0.5,
5962
left: Default::default(),
6063
right: Default::default(),
61-
};
62-
filter.update();
63-
filter
64+
sample_rate: 0
65+
}
6466
}
6567
}
6668

6769
impl EffectRenderTrait for $name {
68-
fn render(&mut self, input: &[(f32, f32)], output: &mut [(f32, f32)]) {
70+
fn render(&mut self, sample_rate: u32, input: &[(f32, f32)], output: &mut [(f32, f32)]) {
71+
if self.sample_rate != sample_rate {
72+
self.sample_rate = sample_rate;
73+
74+
self.left.tune(
75+
$kind,
76+
self.cutoff_frequency_hz / self.sample_rate as f32,
77+
self.gain,
78+
self.quality
79+
);
80+
81+
self.right.tune(
82+
$kind,
83+
self.cutoff_frequency_hz / self.sample_rate as f32,
84+
self.gain,
85+
self.quality
86+
);
87+
}
88+
6989
for ((input_left, input_right), (output_left, output_right)) in input.iter().zip(output) {
7090
*output_left = self.left.feed(*input_left);
7191
*output_right = self.right.feed(*input_right);
@@ -78,7 +98,7 @@ macro_rules! define_filter_effect {
7898
#[inline]
7999
pub fn set_gain(&mut self, gain: f32) {
80100
self.gain = gain;
81-
self.update();
101+
self.sample_rate = 0;
82102
}
83103

84104
/// Returns filter's gain coefficient.
@@ -92,7 +112,7 @@ macro_rules! define_filter_effect {
92112
#[inline]
93113
pub fn set_cutoff_frequency_hz(&mut self, freq: f32) {
94114
self.cutoff_frequency_hz = freq;
95-
self.update();
115+
self.sample_rate = 0;
96116
}
97117

98118
/// Returns a cutoff frequency of the filter in Hertz.
@@ -106,30 +126,14 @@ macro_rules! define_filter_effect {
106126
#[inline]
107127
pub fn set_quality(&mut self, quality: f32) {
108128
self.quality = quality;
109-
self.update();
129+
self.sample_rate = 0;
110130
}
111131

112132
/// Returns the quality of the filter.
113133
#[inline]
114134
pub fn quality(&self) -> f32 {
115135
self.quality
116136
}
117-
118-
fn update(&mut self) {
119-
self.left.tune(
120-
$kind,
121-
self.cutoff_frequency_hz / SAMPLE_RATE as f32,
122-
self.gain,
123-
self.quality
124-
);
125-
126-
self.right.tune(
127-
$kind,
128-
self.cutoff_frequency_hz / SAMPLE_RATE as f32,
129-
self.gain,
130-
self.quality
131-
)
132-
}
133137
}
134138
};
135139
}

0 commit comments

Comments
 (0)