Skip to main content

wavyzon_plugin/
filter.rs

1//! Custom audio filters contributed by plugins.
2//!
3//! Clients enable them through `filters.pluginFilters.<name>` on a player
4//! update, exactly like Lavalink's `pluginFilters`:
5//!
6//! ```json
7//! { "filters": { "pluginFilters": { "bitcrush": { "bits": 6 } } } }
8//! ```
9
10use serde_json::Value;
11
12use crate::Failure;
13
14/// Sample rate of every frame handed to a filter.
15pub const SAMPLE_RATE: u32 = 48_000;
16/// Interleaved channels.
17pub const CHANNELS: usize = 2;
18/// One 20 ms frame: 960 samples per channel, interleaved L/R.
19pub const FRAME_SAMPLES: usize = 960 * CHANNELS;
20
21/// A running filter instance. The player builds one instance per deck (so two
22/// exist during a crossfade), and may call [`FilterFactory::create`] once more
23/// just to validate a configuration.
24///
25/// `process` runs on the real-time send path every 20 ms: it must not block,
26/// allocate heavily or do I/O.
27pub trait AudioFilter: Send {
28    /// Process one interleaved stereo frame in place, values in `-1.0..=1.0`.
29    fn process(&mut self, frame: &mut [f32]);
30
31    /// A new configuration arrived for an already running filter. Return
32    /// `false` to have the player rebuild the filter from the factory instead.
33    fn reconfigure(&mut self, _config: &Value) -> bool {
34        false
35    }
36
37    /// Clear internal state (called on seek and track change).
38    fn reset(&mut self) {}
39}
40
41/// Builds [`AudioFilter`]s from client JSON.
42pub trait FilterFactory: Send + Sync + 'static {
43    /// The key under `pluginFilters` (`"bitcrush"`).
44    fn name(&self) -> &str;
45
46    /// Validate `config` and build an instance. Errors are returned to the
47    /// client as `400 Bad Request`.
48    fn create(&self, config: &Value) -> Result<Box<dyn AudioFilter>, Failure>;
49}