use std::sync::RwLock;
use crate::analysis::{
Analyser, AnalyserRingBuffer, DEFAULT_FFT_SIZE, DEFAULT_MAX_DECIBELS, DEFAULT_MIN_DECIBELS,
DEFAULT_SMOOTHING_TIME_CONSTANT,
};
use crate::context::{AudioContextRegistration, BaseAudioContext};
use crate::render::{AudioParamValues, AudioProcessor, AudioRenderQuantum, RenderScope};
use super::{AudioNode, ChannelConfig, ChannelConfigOptions, ChannelInterpretation};
#[derive(Clone, Debug)]
pub struct AnalyserOptions {
pub fft_size: usize,
pub max_decibels: f64,
pub min_decibels: f64,
pub smoothing_time_constant: f64,
pub channel_config: ChannelConfigOptions,
}
impl Default for AnalyserOptions {
fn default() -> Self {
Self {
fft_size: DEFAULT_FFT_SIZE,
max_decibels: DEFAULT_MAX_DECIBELS,
min_decibels: DEFAULT_MIN_DECIBELS,
smoothing_time_constant: DEFAULT_SMOOTHING_TIME_CONSTANT,
channel_config: ChannelConfigOptions::default(),
}
}
}
pub struct AnalyserNode {
registration: AudioContextRegistration,
channel_config: ChannelConfig,
analyser: RwLock<Analyser>,
}
impl AudioNode for AnalyserNode {
fn registration(&self) -> &AudioContextRegistration {
&self.registration
}
fn channel_config(&self) -> &ChannelConfig {
&self.channel_config
}
fn number_of_inputs(&self) -> usize {
1
}
fn number_of_outputs(&self) -> usize {
1
}
}
impl AnalyserNode {
pub fn new<C: BaseAudioContext>(context: &C, options: AnalyserOptions) -> Self {
context.register(move |registration| {
let fft_size = options.fft_size;
let smoothing_time_constant = options.smoothing_time_constant;
let min_decibels = options.min_decibels;
let max_decibels = options.max_decibels;
let mut analyser = Analyser::new();
analyser.set_fft_size(fft_size);
analyser.set_smoothing_time_constant(smoothing_time_constant);
analyser.set_min_decibels(min_decibels);
analyser.set_max_decibels(max_decibels);
let render = AnalyserRenderer {
ring_buffer: analyser.get_ring_buffer_clone(),
};
let node = AnalyserNode {
registration,
channel_config: options.channel_config.into(),
analyser: RwLock::new(analyser),
};
(node, Box::new(render))
})
}
pub fn fft_size(&self) -> usize {
self.analyser.read().unwrap().fft_size()
}
pub fn set_fft_size(&self, fft_size: usize) {
self.analyser.write().unwrap().set_fft_size(fft_size);
}
pub fn smoothing_time_constant(&self) -> f64 {
self.analyser.read().unwrap().smoothing_time_constant()
}
pub fn set_smoothing_time_constant(&self, value: f64) {
self.analyser
.write()
.unwrap()
.set_smoothing_time_constant(value);
}
pub fn min_decibels(&self) -> f64 {
self.analyser.read().unwrap().min_decibels()
}
pub fn set_min_decibels(&self, value: f64) {
self.analyser.write().unwrap().set_min_decibels(value);
}
pub fn max_decibels(&self) -> f64 {
self.analyser.read().unwrap().max_decibels()
}
pub fn set_max_decibels(&self, value: f64) {
self.analyser.write().unwrap().set_max_decibels(value);
}
pub fn frequency_bin_count(&self) -> usize {
self.analyser.read().unwrap().frequency_bin_count()
}
pub fn get_float_time_domain_data(&self, buffer: &mut [f32]) {
self.analyser
.write()
.unwrap()
.get_float_time_domain_data(buffer);
}
pub fn get_byte_time_domain_data(&self, buffer: &mut [u8]) {
self.analyser
.write()
.unwrap()
.get_byte_time_domain_data(buffer);
}
pub fn get_float_frequency_data(&self, buffer: &mut [f32]) {
let current_time = self.registration.context().current_time();
self.analyser
.write()
.unwrap()
.get_float_frequency_data(buffer, current_time);
}
pub fn get_byte_frequency_data(&self, buffer: &mut [u8]) {
let current_time = self.registration.context().current_time();
self.analyser
.write()
.unwrap()
.get_byte_frequency_data(buffer, current_time);
}
}
struct AnalyserRenderer {
ring_buffer: AnalyserRingBuffer,
}
impl AudioProcessor for AnalyserRenderer {
fn process(
&mut self,
inputs: &[AudioRenderQuantum],
outputs: &mut [AudioRenderQuantum],
_params: AudioParamValues<'_>,
_scope: &RenderScope,
) -> bool {
let input = &inputs[0];
let output = &mut outputs[0];
*output = input.clone();
let mut mono = input.clone();
mono.mix(1, ChannelInterpretation::Speakers);
let data = mono.channel_data(0).as_ref();
self.ring_buffer.write(data);
false
}
}