A decentralized music tracking and discovery platform built on AT Protocol 馃幍
at feat/pgpull 1060 lines 30 kB view raw
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}