forked from
rocksky.app/rocksky
A decentralized music tracking and discovery platform built on AT Protocol 馃幍
1use base64::prelude::*;
2use rand::seq::SliceRandom;
3use rand::thread_rng;
4use serde_json::json;
5use std::f32::consts::PI;
6use std::io::Cursor;
7use symphonia::core::audio::SampleBuffer;
8use symphonia::core::codecs::{CODEC_TYPE_NULL, DecoderOptions};
9use symphonia::core::formats::FormatOptions;
10use symphonia::core::io::{MediaSource, MediaSourceStream};
11use symphonia::core::meta::MetadataOptions;
12use symphonia::core::probe::Hint;
13use wasm_bindgen::prelude::*;
14
15#[wasm_bindgen]
16pub fn extract_audio_metadata(data: &[u8]) -> JsValue {
17 let media_source: Box<dyn MediaSource> = Box::new(Cursor::new(data.to_vec()));
18 let mss = MediaSourceStream::new(media_source, Default::default());
19
20 let hint = Hint::new();
21
22 let meta_opts = MetadataOptions::default();
23 let format_opts = FormatOptions::default();
24
25 let mut probed =
26 match symphonia::default::get_probe().format(&hint, mss, &format_opts, &meta_opts) {
27 Ok(probed) => probed,
28 Err(_) => return JsValue::NULL, // Return null if the format is unsupported
29 };
30
31 let mut metadata = json!({});
32
33 // Extract metadata tags
34 if let Some(track) = probed.format.metadata().current() {
35 for tag in track.tags() {
36 if let Some(key) = tag.std_key {
37 metadata[&format!("{:?}", key)] = serde_json::Value::String(tag.value.to_string());
38 }
39 }
40
41 // Extract album art if available
42 if let Some(cover) = track
43 .visuals()
44 .iter()
45 .find(|v| v.media_type.starts_with("image/"))
46 {
47 let base64_image = BASE64_STANDARD.encode(&cover.data);
48 let mime_type = &cover.media_type;
49 metadata["album_art"] = json!({
50 "data": base64_image,
51 "mime": mime_type,
52 });
53 }
54 }
55
56 if let Some(track) = probed.format.tracks().first() {
57 if let Some(duration) = track.codec_params.n_frames {
58 if let Some(sample_rate) = track.codec_params.sample_rate {
59 let duration_seconds = duration as f64 / sample_rate as f64;
60 metadata["Duration"] = json!(duration_seconds);
61 }
62 }
63 }
64
65 JsValue::from_str(serde_json::to_string(&metadata).unwrap().as_str())
66}
67
68#[wasm_bindgen]
69pub struct AudioDecoder {
70 pcm_data: Vec<f32>,
71 sample_rate: u32,
72 channels: u16,
73}
74
75#[wasm_bindgen]
76impl AudioDecoder {
77 #[wasm_bindgen(constructor)]
78 pub fn new() -> Self {
79 Self {
80 pcm_data: Vec::new(),
81 sample_rate: 44100,
82 channels: 2,
83 }
84 }
85
86 #[wasm_bindgen]
87 pub fn decode(&mut self, audio_data: &[u8], ext: &str) -> Result<(), JsValue> {
88 let media_source: Box<dyn MediaSource> = Box::new(Cursor::new(audio_data.to_vec()));
89
90 let mss = MediaSourceStream::new(media_source, Default::default());
91
92 let mut hint = Hint::new();
93 hint.with_extension(ext);
94
95 let probed = symphonia::default::get_probe()
96 .format(
97 &hint,
98 mss,
99 &FormatOptions {
100 enable_gapless: false,
101 ..Default::default()
102 },
103 &MetadataOptions::default(),
104 )
105 .map_err(|e| JsValue::from_str(&format!("Failed to read format: {}", e)))?;
106
107 let mut format = probed.format;
108 let track = format
109 .default_track()
110 .ok_or_else(|| JsValue::from_str("No default track found"))?;
111 let codec_params = &track.codec_params;
112
113 if codec_params.codec == CODEC_TYPE_NULL {
114 return Err(JsValue::from_str("Unsupported codec"));
115 }
116
117 let mut decoder = symphonia::default::get_codecs()
118 .make(&codec_params, &DecoderOptions::default())
119 .map_err(|e| JsValue::from_str(&format!("Failed to create decoder: {}", e)))?;
120
121 self.sample_rate = codec_params.sample_rate.unwrap_or(44100);
122 self.channels = 2;
123
124 while let Ok(packet) = format.next_packet() {
125 let decoded = decoder
126 .decode(&packet)
127 .map_err(|e| JsValue::from_str(&format!("Decode error: {}", e)))?;
128 let mut sample_buf =
129 SampleBuffer::<f32>::new(decoded.capacity() as u64, *decoded.spec());
130 sample_buf.copy_interleaved_ref(decoded);
131
132 self.pcm_data.extend(sample_buf.samples());
133 }
134
135 Ok(())
136 }
137
138 #[wasm_bindgen]
139 pub fn get_pcm_data(&self) -> Vec<f32> {
140 self.pcm_data.clone()
141 }
142
143 #[wasm_bindgen]
144 pub fn get_sample_rate(&self) -> u32 {
145 self.sample_rate
146 }
147
148 #[wasm_bindgen]
149 pub fn get_channels(&self) -> u16 {
150 self.channels
151 }
152}
153
154#[wasm_bindgen]
155pub enum FadeCurve {
156 Linear,
157 Exponential,
158 Logarithmic,
159}
160
161/// Crossfades between two audio buffers using a specified fade curve
162#[wasm_bindgen]
163pub fn crossfade(
164 buffer_a: &[f32],
165 buffer_b: &[f32],
166 fade_duration: usize,
167 fade_curve: FadeCurve,
168) -> Vec<f32> {
169 let len_a = buffer_a.len();
170 let len_b = buffer_b.len();
171
172 let crossfade_len = fade_duration.min(len_a).min(len_b);
173 let mut output = Vec::with_capacity(len_a + len_b - crossfade_len);
174
175 // Copy the first part of buffer A
176 output.extend_from_slice(&buffer_a[..len_a - crossfade_len]);
177
178 // Apply the crossfade
179 for i in 0..crossfade_len {
180 let t = i as f32 / crossfade_len as f32;
181 let (fade_out, fade_in) = match fade_curve {
182 FadeCurve::Linear => (1.0 - t, t),
183 FadeCurve::Exponential => ((1.0 - t).powi(2), t.powi(2)),
184 FadeCurve::Logarithmic => (1.0 - t.ln_1p(), t.ln_1p()),
185 };
186
187 let mixed_sample = buffer_a[len_a - crossfade_len + i] * fade_out + buffer_b[i] * fade_in;
188
189 output.push(mixed_sample);
190 }
191
192 // Copy the remaining part of buffer B
193 output.extend_from_slice(&buffer_b[crossfade_len..]);
194
195 output
196}
197
198#[wasm_bindgen]
199pub struct BiquadFilter {
200 a0: f32,
201 a1: f32,
202 a2: f32,
203 b0: f32,
204 b1: f32,
205 b2: f32,
206 x1: f32,
207 x2: f32,
208 y1: f32,
209 y2: f32,
210}
211
212#[wasm_bindgen]
213impl BiquadFilter {
214 // Create a new peaking EQ filter
215 #[wasm_bindgen]
216 pub fn peaking_eq(sample_rate: f32, frequency: f32, q: f32, gain_db: f32) -> Self {
217 let omega = 2.0 * PI * frequency / sample_rate;
218 let alpha = omega.sin() / (2.0 * q);
219 let a = 10.0_f32.powf(gain_db / 40.0);
220
221 let cos_omega = omega.cos();
222
223 let b0 = 1.0 + alpha * a;
224 let b1 = -2.0 * cos_omega;
225 let b2 = 1.0 - alpha * a;
226 let a0 = 1.0 + alpha / a;
227 let a1 = -2.0 * cos_omega;
228 let a2 = 1.0 - alpha / a;
229
230 BiquadFilter {
231 a0,
232 a1,
233 a2,
234 b0,
235 b1,
236 b2,
237 x1: 0.0,
238 x2: 0.0,
239 y1: 0.0,
240 y2: 0.0,
241 }
242 }
243
244 // Process a single sample through the filter
245 #[wasm_bindgen]
246 pub fn process(&mut self, input: f32) -> f32 {
247 let output = (self.b0 / self.a0) * input
248 + (self.b1 / self.a0) * self.x1
249 + (self.b2 / self.a0) * self.x2
250 - (self.a1 / self.a0) * self.y1
251 - (self.a2 / self.a0) * self.y2;
252
253 // Update filter state
254 self.x2 = self.x1;
255 self.x1 = input;
256 self.y2 = self.y1;
257 self.y1 = output;
258
259 output
260 }
261
262 // Update filter parameters
263 #[wasm_bindgen]
264 pub fn update_parameters(&mut self, sample_rate: f32, frequency: f32, q: f32, gain_db: f32) {
265 let omega = 2.0 * PI * frequency / sample_rate;
266 let alpha = omega.sin() / (2.0 * q);
267 let a = 10.0_f32.powf(gain_db / 40.0);
268
269 let cos_omega = omega.cos();
270
271 self.b0 = 1.0 + alpha * a;
272 self.b1 = -2.0 * cos_omega;
273 self.b2 = 1.0 - alpha * a;
274 self.a0 = 1.0 + alpha / a;
275 self.a1 = -2.0 * cos_omega;
276 self.a2 = 1.0 - alpha / a;
277 }
278}
279
280// 12-band equalizer
281#[wasm_bindgen]
282pub struct Equalizer {
283 bands: Vec<BiquadFilter>,
284 sample_rate: f32,
285}
286
287#[wasm_bindgen]
288impl Equalizer {
289 // Create a new 12-band equalizer with standard frequency bands
290 #[wasm_bindgen(constructor)]
291 pub fn new(sample_rate: f32) -> Self {
292 // Standard frequency bands for a 12-band EQ (in Hz)
293 let frequencies = [
294 31.0, 62.0, 125.0, 250.0, 500.0, 1000.0, 2000.0, 4000.0, 8000.0, 12000.0, 16000.0,
295 20000.0,
296 ];
297
298 let mut bands = Vec::with_capacity(frequencies.len());
299
300 // Create a filter for each frequency band with default gain of 0 dB
301 for &freq in &frequencies {
302 bands.push(BiquadFilter::peaking_eq(sample_rate, freq, 1.414, 0.0));
303 }
304
305 Equalizer { bands, sample_rate }
306 }
307
308 // Set gain for a specific band
309 #[wasm_bindgen]
310 pub fn set_band_gain(&mut self, band_index: usize, gain_db: f32) {
311 if band_index >= self.bands.len() {
312 return;
313 }
314
315 // Standard frequency bands
316 let frequencies = [
317 31.0, 62.0, 125.0, 250.0, 500.0, 1000.0, 2000.0, 4000.0, 8000.0, 12000.0, 16000.0,
318 20000.0,
319 ];
320
321 // Update the filter parameters with the new gain
322 let frequency = frequencies[band_index];
323 let q = 1.414; // Standard Q value for EQ bands
324 self.bands[band_index].update_parameters(self.sample_rate, frequency, q, gain_db);
325 }
326
327 // Process a single sample through all EQ bands
328 #[wasm_bindgen]
329 pub fn process(&mut self, input: f32) -> f32 {
330 let mut output = input;
331
332 // Pass the input through each band filter in series
333 for band in &mut self.bands {
334 output = band.process(output);
335 }
336
337 output
338 }
339
340 // Process a buffer of samples
341 #[wasm_bindgen]
342 pub fn process_buffer(&mut self, input_buffer: &[f32], output_buffer: &mut [f32]) {
343 assert_eq!(input_buffer.len(), output_buffer.len());
344
345 for i in 0..input_buffer.len() {
346 output_buffer[i] = self.process(input_buffer[i]);
347 }
348 }
349}
350
351#[wasm_bindgen]
352pub struct AudioFilter {
353 sample_rate: f32,
354 // Common filter state variables
355 x1: f32,
356 x2: f32,
357 y1: f32,
358 y2: f32,
359 // Additional states for higher order filters
360 x3: f32,
361 x4: f32,
362 y3: f32,
363 y4: f32,
364}
365
366#[wasm_bindgen]
367impl AudioFilter {
368 #[wasm_bindgen(constructor)]
369 pub fn new(sample_rate: f32) -> Self {
370 Self {
371 sample_rate,
372 x1: 0.0,
373 x2: 0.0,
374 y1: 0.0,
375 y2: 0.0,
376 x3: 0.0,
377 x4: 0.0,
378 y3: 0.0,
379 y4: 0.0,
380 }
381 }
382
383 #[wasm_bindgen]
384 pub fn reset(&mut self) {
385 self.x1 = 0.0;
386 self.x2 = 0.0;
387 self.y1 = 0.0;
388 self.y2 = 0.0;
389 self.x3 = 0.0;
390 self.x4 = 0.0;
391 self.y3 = 0.0;
392 self.y4 = 0.0;
393 }
394}
395
396#[wasm_bindgen]
397pub struct LowShelfFilter {
398 filter: AudioFilter,
399 a1: f32,
400 a2: f32,
401 b0: f32,
402 b1: f32,
403 b2: f32,
404}
405
406#[wasm_bindgen]
407impl LowShelfFilter {
408 #[wasm_bindgen(constructor)]
409 pub fn new(sample_rate: f32, frequency: f32, gain_db: f32, q: f32) -> Self {
410 let mut filter = Self {
411 filter: AudioFilter::new(sample_rate),
412 a1: 0.0,
413 a2: 0.0,
414 b0: 0.0,
415 b1: 0.0,
416 b2: 0.0,
417 };
418 filter.set_parameters(frequency, gain_db, q);
419 filter
420 }
421
422 #[wasm_bindgen]
423 pub fn set_parameters(&mut self, frequency: f32, gain_db: f32, q: f32) {
424 let a = 10.0_f32.powf(gain_db / 40.0);
425 let omega = 2.0 * std::f32::consts::PI * frequency / self.filter.sample_rate;
426 let sin_omega = omega.sin();
427 let cos_omega = omega.cos();
428 let alpha = sin_omega / (2.0 * q);
429 let beta = (a + 1.0 / a).sqrt() * 2.0 * alpha;
430
431 let b0 = a * ((a + 1.0) - (a - 1.0) * cos_omega + beta);
432 let b1 = 2.0 * a * ((a - 1.0) - (a + 1.0) * cos_omega);
433 let b2 = a * ((a + 1.0) - (a - 1.0) * cos_omega - beta);
434 let a0 = (a + 1.0) + (a - 1.0) * cos_omega + beta;
435 let a1 = -2.0 * ((a - 1.0) + (a + 1.0) * cos_omega);
436 let a2 = (a + 1.0) + (a - 1.0) * cos_omega - beta;
437
438 self.b0 = b0 / a0;
439 self.b1 = b1 / a0;
440 self.b2 = b2 / a0;
441 self.a1 = a1 / a0;
442 self.a2 = a2 / a0;
443 }
444
445 #[wasm_bindgen]
446 pub fn process(&mut self, input: f32) -> f32 {
447 let output = self.b0 * input + self.b1 * self.filter.x1 + self.b2 * self.filter.x2
448 - self.a1 * self.filter.y1
449 - self.a2 * self.filter.y2;
450
451 self.filter.x2 = self.filter.x1;
452 self.filter.x1 = input;
453 self.filter.y2 = self.filter.y1;
454 self.filter.y1 = output;
455
456 output
457 }
458
459 #[wasm_bindgen]
460 pub fn reset(&mut self) {
461 self.filter.reset();
462 }
463}
464
465#[wasm_bindgen]
466pub struct HighShelfFilter {
467 filter: AudioFilter,
468 a1: f32,
469 a2: f32,
470 b0: f32,
471 b1: f32,
472 b2: f32,
473}
474
475#[wasm_bindgen]
476impl HighShelfFilter {
477 #[wasm_bindgen(constructor)]
478 pub fn new(sample_rate: f32, frequency: f32, gain_db: f32, q: f32) -> Self {
479 let mut filter = Self {
480 filter: AudioFilter::new(sample_rate),
481 a1: 0.0,
482 a2: 0.0,
483 b0: 0.0,
484 b1: 0.0,
485 b2: 0.0,
486 };
487 filter.set_parameters(frequency, gain_db, q);
488 filter
489 }
490
491 #[wasm_bindgen]
492 pub fn set_parameters(&mut self, frequency: f32, gain_db: f32, q: f32) {
493 let a = 10.0_f32.powf(gain_db / 40.0);
494 let omega = 2.0 * std::f32::consts::PI * frequency / self.filter.sample_rate;
495 let sin_omega = omega.sin();
496 let cos_omega = omega.cos();
497 let alpha = sin_omega / (2.0 * q);
498 let beta = (a + 1.0 / a).sqrt() * 2.0 * alpha;
499
500 let b0 = a * ((a + 1.0) + (a - 1.0) * cos_omega + beta);
501 let b1 = -2.0 * a * ((a - 1.0) + (a + 1.0) * cos_omega);
502 let b2 = a * ((a + 1.0) + (a - 1.0) * cos_omega - beta);
503 let a0 = (a + 1.0) - (a - 1.0) * cos_omega + beta;
504 let a1 = 2.0 * ((a - 1.0) - (a + 1.0) * cos_omega);
505 let a2 = (a + 1.0) - (a - 1.0) * cos_omega - beta;
506
507 self.b0 = b0 / a0;
508 self.b1 = b1 / a0;
509 self.b2 = b2 / a0;
510 self.a1 = a1 / a0;
511 self.a2 = a2 / a0;
512 }
513
514 #[wasm_bindgen]
515 pub fn process(&mut self, input: f32) -> f32 {
516 let output = self.b0 * input + self.b1 * self.filter.x1 + self.b2 * self.filter.x2
517 - self.a1 * self.filter.y1
518 - self.a2 * self.filter.y2;
519
520 self.filter.x2 = self.filter.x1;
521 self.filter.x1 = input;
522 self.filter.y2 = self.filter.y1;
523 self.filter.y1 = output;
524
525 output
526 }
527
528 #[wasm_bindgen]
529 pub fn reset(&mut self) {
530 self.filter.reset();
531 }
532}
533
534#[wasm_bindgen]
535pub struct BandPassFilter {
536 filter: AudioFilter,
537 a1: f32,
538 a2: f32,
539 b0: f32,
540 b1: f32,
541 b2: f32,
542}
543
544#[wasm_bindgen]
545impl BandPassFilter {
546 #[wasm_bindgen(constructor)]
547 pub fn new(sample_rate: f32, frequency: f32, q: f32) -> Self {
548 let mut filter = Self {
549 filter: AudioFilter::new(sample_rate),
550 a1: 0.0,
551 a2: 0.0,
552 b0: 0.0,
553 b1: 0.0,
554 b2: 0.0,
555 };
556 filter.set_parameters(frequency, q);
557 filter
558 }
559
560 #[wasm_bindgen]
561 pub fn set_parameters(&mut self, frequency: f32, q: f32) {
562 let omega = 2.0 * std::f32::consts::PI * frequency / self.filter.sample_rate;
563 let sin_omega = omega.sin();
564 let cos_omega = omega.cos();
565 let alpha = sin_omega / (2.0 * q);
566
567 let b0 = alpha;
568 let b1 = 0.0;
569 let b2 = -alpha;
570 let a0 = 1.0 + alpha;
571 let a1 = -2.0 * cos_omega;
572 let a2 = 1.0 - alpha;
573
574 self.b0 = b0 / a0;
575 self.b1 = b1 / a0;
576 self.b2 = b2 / a0;
577 self.a1 = a1 / a0;
578 self.a2 = a2 / a0;
579 }
580
581 #[wasm_bindgen]
582 pub fn process(&mut self, input: f32) -> f32 {
583 let output = self.b0 * input + self.b1 * self.filter.x1 + self.b2 * self.filter.x2
584 - self.a1 * self.filter.y1
585 - self.a2 * self.filter.y2;
586
587 self.filter.x2 = self.filter.x1;
588 self.filter.x1 = input;
589 self.filter.y2 = self.filter.y1;
590 self.filter.y1 = output;
591
592 output
593 }
594
595 #[wasm_bindgen]
596 pub fn reset(&mut self) {
597 self.filter.reset();
598 }
599}
600
601#[wasm_bindgen]
602pub struct BesselFilter {
603 filter: AudioFilter,
604 order: usize,
605 // Coefficients for sections
606 a: Vec<Vec<f32>>,
607 b: Vec<Vec<f32>>,
608}
609
610#[wasm_bindgen]
611impl BesselFilter {
612 #[wasm_bindgen(constructor)]
613 pub fn new(sample_rate: f32, cutoff_frequency: f32, order: usize) -> Self {
614 if order != 4 && order != 8 {
615 panic!("Only 4th and 8th order Bessel filters are implemented");
616 }
617
618 let mut filter = Self {
619 filter: AudioFilter::new(sample_rate),
620 order,
621 a: vec![],
622 b: vec![],
623 };
624 filter.set_parameters(cutoff_frequency);
625 filter
626 }
627
628 #[wasm_bindgen]
629 pub fn set_parameters(&mut self, cutoff_frequency: f32) {
630 let fs = self.filter.sample_rate;
631 let fc = cutoff_frequency;
632
633 // Clear previous coefficients
634 self.a = vec![];
635 self.b = vec![];
636
637 // Normalized frequency
638 let omega_c = 2.0 * std::f32::consts::PI * fc / fs;
639
640 // Bessel polynomials for 4th and 8th order
641 // These are the poles of the normalized (omega_c = 1) Bessel filter
642 let poles = if self.order == 4 {
643 vec![
644 (-0.6572111112819416, 0.8301614350048806),
645 (-0.6572111112819416, -0.8301614350048806),
646 (-0.9047587967882449, 0.2709187330038746),
647 (-0.9047587967882449, -0.2709187330038746),
648 ]
649 } else {
650 // 8th order
651 vec![
652 (-0.5905759446119192, 0.9072067564574548),
653 (-0.5905759446119192, -0.9072067564574548),
654 (-0.6707106781186548, 0.7937387988730166),
655 (-0.6707106781186548, -0.7937387988730166),
656 (-0.7996541858328288, 0.6000376420593046),
657 (-0.7996541858328288, -0.6000376420593046),
658 (-0.8717401485096066, 0.3349881501782813),
659 (-0.8717401485096066, -0.3349881501782813),
660 ]
661 };
662
663 // Create second-order sections from complex conjugate pairs
664 for section in poles.chunks(2) {
665 let (real, imag) = section[0];
666
667 // Bilinear transform to map s-plane to z-plane
668 let c = 1.0 / (omega_c * 2.0);
669 let d = 1.0 + real * c;
670 let e = real * real + imag * imag;
671
672 let b0 = 1.0 / d;
673 let b1 = 2.0 / d;
674 let b2 = 1.0 / d;
675
676 let a0 = 1.0;
677 let a1 = 2.0 * (1.0 - e * c * c) / d;
678 let a2 = (1.0 - 2.0 * real * c + e * c * c) / d;
679
680 self.b.push(vec![b0, b1, b2]);
681 self.a.push(vec![a0, a1, a2]);
682 }
683 }
684
685 #[wasm_bindgen]
686 pub fn process(&mut self, input: f32) -> f32 {
687 let num_sections = self.order / 2;
688 let mut output = input;
689
690 for i in 0..num_sections {
691 let x = output;
692
693 output =
694 self.b[i][0] * x + self.b[i][1] * self.filter.x1 + self.b[i][2] * self.filter.x2
695 - self.a[i][1] * self.filter.y1
696 - self.a[i][2] * self.filter.y2;
697
698 self.filter.x2 = self.filter.x1;
699 self.filter.x1 = x;
700 self.filter.y2 = self.filter.y1;
701 self.filter.y1 = output;
702 }
703
704 output
705 }
706
707 #[wasm_bindgen]
708 pub fn reset(&mut self) {
709 self.filter.reset();
710 }
711}
712
713#[wasm_bindgen]
714pub struct LinkwitzRileyFilter {
715 filter: AudioFilter,
716 // For 8th order (48 dB/Oct) we need 4 biquad sections
717 a: Vec<Vec<f32>>,
718 b: Vec<Vec<f32>>,
719 state: Vec<Vec<f32>>, // x1, x2, y1, y2 for each section
720}
721
722#[wasm_bindgen]
723impl LinkwitzRileyFilter {
724 #[wasm_bindgen(constructor)]
725 pub fn new(sample_rate: f32, cutoff_frequency: f32, filter_type: FilterType) -> Self {
726 let mut filter = Self {
727 filter: AudioFilter::new(sample_rate),
728 a: vec![vec![0.0; 3]; 4], // 4 biquad sections, each with 3 coefficients
729 b: vec![vec![0.0; 3]; 4],
730 state: vec![vec![0.0; 4]; 4], // 4 states per section
731 };
732 filter.set_parameters(cutoff_frequency, filter_type);
733 filter
734 }
735
736 #[wasm_bindgen]
737 pub fn set_parameters(&mut self, cutoff_frequency: f32, filter_type: FilterType) {
738 let omega_c = 2.0 * std::f32::consts::PI * cutoff_frequency / self.filter.sample_rate;
739 let k = omega_c.tan();
740
741 // Butterworth poles for 8th order (48 dB/Oct)
742 let poles = [
743 (-0.9808, 0.1951),
744 (-0.9808, -0.1951),
745 (-0.8315, 0.5556),
746 (-0.8315, -0.5556),
747 (-0.5556, 0.8315),
748 (-0.5556, -0.8315),
749 (-0.1951, 0.9808),
750 (-0.1951, -0.9808),
751 ];
752
753 // Process each section (4 sections for 8th order)
754 for i in 0..4 {
755 let (real, imag) = poles[i * 2];
756
757 // Bilinear transform
758 let d = 1.0 + real * k + (real * real + imag * imag) * k * k;
759
760 match filter_type {
761 FilterType::LowPass => {
762 let k_squared = k * k;
763 self.b[i][0] = k_squared / d;
764 self.b[i][1] = 2.0 * k_squared / d;
765 self.b[i][2] = k_squared / d;
766 self.a[i][0] = 1.0;
767 self.a[i][1] = 2.0 * (k_squared - 1.0) / d;
768 self.a[i][2] = (1.0 - real * k + (real * real + imag * imag) * k * k) / d;
769 }
770 FilterType::HighPass => {
771 self.b[i][0] = 1.0 / d;
772 self.b[i][1] = -2.0 / d;
773 self.b[i][2] = 1.0 / d;
774 self.a[i][0] = 1.0;
775 self.a[i][1] = 2.0 * (k * k - 1.0) / d;
776 self.a[i][2] = (1.0 - real * k + (real * real + imag * imag) * k * k) / d;
777 }
778 }
779 }
780 }
781
782 #[wasm_bindgen]
783 pub fn process(&mut self, input: f32) -> f32 {
784 let mut output = input;
785
786 for i in 0..4 {
787 let x = output;
788
789 // Apply the biquad filter
790 output = self.b[i][0] * x
791 + self.b[i][1] * self.state[i][0]
792 + self.b[i][2] * self.state[i][1]
793 - self.a[i][1] * self.state[i][2]
794 - self.a[i][2] * self.state[i][3];
795
796 // Update states
797 self.state[i][1] = self.state[i][0];
798 self.state[i][0] = x;
799 self.state[i][3] = self.state[i][2];
800 self.state[i][2] = output;
801 }
802
803 output
804 }
805
806 #[wasm_bindgen]
807 pub fn reset(&mut self) {
808 for i in 0..4 {
809 for j in 0..4 {
810 self.state[i][j] = 0.0;
811 }
812 }
813 }
814}
815
816#[wasm_bindgen]
817pub enum FilterType {
818 LowPass,
819 HighPass,
820}
821
822#[wasm_bindgen]
823pub struct Playlist {
824 tracks: Vec<String>,
825 current_track: usize,
826}
827
828#[wasm_bindgen]
829impl Playlist {
830 #[wasm_bindgen(constructor)]
831 pub fn new() -> Self {
832 Self {
833 tracks: Vec::new(),
834 current_track: 0,
835 }
836 }
837
838 #[wasm_bindgen]
839 pub fn add_track(&mut self, track: &str) -> usize {
840 self.tracks.push(track.to_string());
841 self.tracks.len()
842 }
843
844 #[wasm_bindgen]
845 pub fn next_track(&mut self) -> String {
846 if self.tracks.is_empty() {
847 return String::new();
848 }
849
850 self.current_track = (self.current_track + 1) % self.tracks.len();
851 self.tracks[self.current_track].clone()
852 }
853
854 #[wasm_bindgen]
855 pub fn previous_track(&mut self) -> String {
856 if self.tracks.is_empty() {
857 return String::new();
858 }
859
860 if self.current_track == 0 {
861 self.current_track = self.tracks.len() - 1;
862 } else {
863 self.current_track -= 1;
864 }
865
866 self.tracks[self.current_track].clone()
867 }
868
869 #[wasm_bindgen]
870 pub fn current_track(&self) -> String {
871 if self.tracks.is_empty() {
872 return String::new();
873 }
874
875 self.tracks[self.current_track].clone()
876 }
877
878 #[wasm_bindgen]
879 pub fn clear(&mut self) {
880 self.tracks.clear();
881 self.current_track = 0;
882 }
883
884 #[wasm_bindgen]
885 pub fn size(&self) -> usize {
886 self.tracks.len()
887 }
888
889 #[wasm_bindgen]
890 pub fn current_index(&self) -> usize {
891 self.current_track
892 }
893
894 #[wasm_bindgen]
895 pub fn set_current_index(&mut self, index: usize) {
896 if index < self.tracks.len() {
897 self.current_track = index;
898 }
899 }
900
901 #[wasm_bindgen]
902 pub fn remove_track(&mut self, index: usize) {
903 if index < self.tracks.len() {
904 self.tracks.remove(index);
905 }
906 }
907
908 #[wasm_bindgen]
909 pub fn get_track(&self, index: usize) -> String {
910 if index < self.tracks.len() {
911 self.tracks[index].clone()
912 } else {
913 String::new()
914 }
915 }
916
917 #[wasm_bindgen]
918
919 pub fn get_tracks(&self) -> js_sys::Array {
920 self.tracks.iter().map(|t| JsValue::from_str(t)).collect()
921 }
922
923 #[wasm_bindgen]
924 pub fn shuffle(&mut self) {
925 let mut rng = thread_rng();
926 self.tracks.shuffle(&mut rng);
927 }
928
929 #[wasm_bindgen]
930 pub fn insert_track(&mut self, index: usize, track: &str) {
931 if index <= self.tracks.len() {
932 self.tracks.insert(index, track.to_string());
933 }
934 }
935
936 #[wasm_bindgen]
937 pub fn get_next_track(&self) -> String {
938 if self.tracks.is_empty() {
939 return String::new();
940 }
941
942 let next_track = (self.current_track + 1) % self.tracks.len();
943 self.tracks[next_track].clone()
944 }
945
946 #[wasm_bindgen]
947 pub fn get_next_tracks(&self) -> js_sys::Array {
948 if self.tracks.is_empty() {
949 return js_sys::Array::new();
950 }
951
952 let current_track = self.current_track;
953 let next_tracks: Vec<String> = self
954 .tracks
955 .iter()
956 .skip(current_track + 1)
957 .cloned()
958 .collect();
959 next_tracks.iter().map(|t| JsValue::from_str(t)).collect()
960 }
961}
962
963#[cfg(test)]
964mod tests {
965 use super::*;
966
967 #[test]
968 fn test_playlist_add_and_get_tracks() {
969 let mut playlist = Playlist::new();
970 playlist.add_track("Track 1");
971 playlist.add_track("Track 2");
972 playlist.add_track("Track 3");
973
974 assert_eq!(playlist.size(), 3);
975 assert_eq!(playlist.get_track(0), "Track 1");
976 assert_eq!(playlist.get_track(1), "Track 2");
977 assert_eq!(playlist.get_track(2), "Track 3");
978 }
979
980 #[test]
981 fn test_playlist_navigation() {
982 let mut playlist = Playlist::new();
983 playlist.add_track("Track 1");
984 playlist.add_track("Track 2");
985 playlist.add_track("Track 3");
986
987 assert_eq!(playlist.current_track(), "Track 1");
988 assert_eq!(playlist.next_track(), "Track 2");
989 assert_eq!(playlist.next_track(), "Track 3");
990 assert_eq!(playlist.next_track(), "Track 1");
991
992 assert_eq!(playlist.previous_track(), "Track 3");
993 assert_eq!(playlist.previous_track(), "Track 2");
994 assert_eq!(playlist.previous_track(), "Track 1");
995 }
996
997 #[test]
998 fn test_playlist_clear() {
999 let mut playlist = Playlist::new();
1000 playlist.add_track("Track 1");
1001 playlist.add_track("Track 2");
1002
1003 assert_eq!(playlist.size(), 2);
1004 playlist.clear();
1005 assert_eq!(playlist.size(), 0);
1006 assert_eq!(playlist.current_track(), "");
1007 }
1008
1009 #[test]
1010 fn test_playlist_remove_track() {
1011 let mut playlist = Playlist::new();
1012 playlist.add_track("Track 1");
1013 playlist.add_track("Track 2");
1014 playlist.add_track("Track 3");
1015
1016 playlist.remove_track(1);
1017 assert_eq!(playlist.size(), 2);
1018 assert_eq!(playlist.get_track(0), "Track 1");
1019 assert_eq!(playlist.get_track(1), "Track 3");
1020 }
1021
1022 #[test]
1023 fn test_playlist_insert_track() {
1024 let mut playlist = Playlist::new();
1025 playlist.add_track("Track 1");
1026 playlist.add_track("Track 3");
1027
1028 playlist.insert_track(1, "Track 2");
1029 assert_eq!(playlist.size(), 3);
1030 assert_eq!(playlist.get_track(0), "Track 1");
1031 assert_eq!(playlist.get_track(1), "Track 2");
1032 assert_eq!(playlist.get_track(2), "Track 3");
1033 }
1034
1035 #[test]
1036 fn test_playlist_set_current_index() {
1037 let mut playlist = Playlist::new();
1038 playlist.add_track("Track 1");
1039 playlist.add_track("Track 2");
1040 playlist.add_track("Track 3");
1041
1042 playlist.set_current_index(2);
1043 assert_eq!(playlist.current_track(), "Track 3");
1044
1045 playlist.set_current_index(0);
1046 assert_eq!(playlist.current_track(), "Track 1");
1047 }
1048
1049 #[test]
1050 fn test_playlist_get_next_track() {
1051 let mut playlist = Playlist::new();
1052 playlist.add_track("Track 1");
1053 playlist.add_track("Track 2");
1054 playlist.add_track("Track 3");
1055
1056 assert_eq!(playlist.get_next_track(), "Track 2");
1057 playlist.next_track();
1058 assert_eq!(playlist.get_next_track(), "Track 3");
1059 }
1060}