1pub mod adpcm_clip_generated;
10#[cfg(all(test, feature = "host"))]
11mod host_tests;
12pub mod pcm_clip_generated;
13
14use core::ops::ControlFlow;
18use core::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
19use core::sync::atomic::{AtomicI32, Ordering};
20use core::time::Duration;
21
22use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal};
23use heapless::Vec;
24
25const I16_ABS_MAX_I64: i64 = -(i16::MIN as i64);
26const ADPCM_ENCODE_BLOCK_ALIGN: usize = 256;
27
28pub const NARROWBAND_8000_HZ: u32 = 8_000;
32pub const VOICE_16000_HZ: u32 = 16_000;
34pub const VOICE_22050_HZ: u32 = 22_050;
39pub const CD_44100_HZ: u32 = 44_100;
41pub const PRO_48000_HZ: u32 = 48_000;
43
44#[derive(Clone, Copy, Debug, PartialEq, Eq)]
57pub struct Volume(i16);
58
59impl Volume {
60 pub const MUTE: Self = Self(0);
62
63 pub const MAX: Self = Self(i16::MAX);
65
66 #[must_use]
73 pub const fn percent(percent: u8) -> Self {
74 let percent = if percent > 100 { 100 } else { percent };
75 let value_i32 = (percent as i32 * i16::MAX as i32) / 100;
76 Self(value_i32 as i16)
77 }
78
79 #[must_use]
89 pub const fn spinal_tap(spinal_tap: u8) -> Self {
90 let spinal_tap = if spinal_tap > 11 { 11 } else { spinal_tap };
91 let percent = match spinal_tap {
92 0 => 0,
93 1 => 1,
94 2 => 3,
95 3 => 6,
96 4 => 13,
97 5 => 25,
98 6 => 35,
99 7 => 50,
100 8 => 71,
101 9 => 89,
102 10 => 100,
103 11 => 100,
104 _ => 100,
105 };
106 Self::percent(percent)
107 }
108
109 #[must_use]
110 pub(crate) const fn to_i16(self) -> i16 {
111 self.0
112 }
113
114 #[must_use]
115 pub(crate) const fn from_i16(value_i16: i16) -> Self {
116 Self(value_i16)
117 }
118}
119
120#[derive(Clone, Copy, Debug, PartialEq, Eq)]
140pub struct Gain(i32);
141
142impl Gain {
143 pub const MUTE: Self = Self(0);
145
146 #[must_use]
153 pub const fn percent(percent: u16) -> Self {
154 let value_i32 = (percent as i32 * i16::MAX as i32) / 100;
155 Self(value_i32)
156 }
157
158 #[must_use]
165 pub const fn db(db: i8) -> Self {
166 const DB_UPPER_LIMIT: i8 = 12;
167 const DB_LOWER_LIMIT: i8 = -96;
168 let db = if db > DB_UPPER_LIMIT {
169 DB_UPPER_LIMIT
170 } else if db < DB_LOWER_LIMIT {
171 DB_LOWER_LIMIT
172 } else {
173 db
174 };
175
176 if db == 0 {
177 return Self::percent(100);
178 }
179
180 const DB_STEP_DOWN_Q15: i32 = 29_205;
182 const DB_STEP_UP_Q15: i32 = 36_781;
183 const ONE_Q15: i32 = 32_768;
184 const ROUND_Q15: i32 = 16_384;
185 let step_q15_i32 = if db > 0 {
186 DB_STEP_UP_Q15
187 } else {
188 DB_STEP_DOWN_Q15
189 };
190 let db_steps_u8 = if db > 0 { db as u8 } else { (-db) as u8 };
191 let mut scale_q15_i32 = ONE_Q15;
192 let mut step_index = 0_u8;
193 while step_index < db_steps_u8 {
195 scale_q15_i32 = (scale_q15_i32 * step_q15_i32 + ROUND_Q15) / ONE_Q15;
196 step_index += 1;
197 }
198
199 let gain_i64 = (i16::MAX as i64 * scale_q15_i32 as i64 + ROUND_Q15 as i64) / ONE_Q15 as i64;
200 let gain_i32 = if gain_i64 > i32::MAX as i64 {
201 i32::MAX
202 } else {
203 gain_i64 as i32
204 };
205 Self(gain_i32)
206 }
207
208 #[must_use]
209 const fn linear(self) -> i32 {
210 self.0
211 }
212}
213
214#[must_use]
215#[doc(hidden)]
216pub const fn __samples_for_duration(duration: core::time::Duration, sample_rate_hz: u32) -> usize {
218 assert!(sample_rate_hz > 0, "sample_rate_hz must be > 0");
219 let sample_rate_hz_u64 = sample_rate_hz as u64;
220 let samples_from_seconds_u64 = duration.as_secs() * sample_rate_hz_u64;
221 let samples_from_subsec_nanos_u64 =
222 (duration.subsec_nanos() as u64 * sample_rate_hz_u64) / 1_000_000_000_u64;
223 let total_samples_u64 = samples_from_seconds_u64 + samples_from_subsec_nanos_u64;
224 assert!(
225 total_samples_u64 <= usize::MAX as u64,
226 "duration/sample_rate result must fit usize"
227 );
228 total_samples_u64 as usize
229}
230
231const fn duration_for_sample_count(sample_count: usize, sample_rate_hz: u32) -> Duration {
232 assert!(sample_rate_hz > 0, "sample_rate_hz must be > 0");
233 let sample_rate_hz_usize = sample_rate_hz as usize;
234 let whole_seconds = sample_count / sample_rate_hz_usize;
235 let subsecond_sample_count = sample_count % sample_rate_hz_usize;
236 let subsecond_nanos =
237 ((subsecond_sample_count as u64) * 1_000_000_000_u64) / sample_rate_hz as u64;
238 Duration::new(whole_seconds as u64, subsecond_nanos as u32)
239}
240
241#[doc(hidden)]
245#[must_use]
246pub const fn __resampled_sample_count(
247 source_sample_count: usize,
248 source_sample_rate_hz: u32,
249 destination_sample_rate_hz: u32,
250) -> usize {
251 assert!(source_sample_count > 0, "source_sample_count must be > 0");
252 assert!(
253 source_sample_rate_hz > 0,
254 "source_sample_rate_hz must be > 0"
255 );
256 assert!(
257 destination_sample_rate_hz > 0,
258 "destination_sample_rate_hz must be > 0"
259 );
260 let destination_sample_count = ((source_sample_count as u64
261 * destination_sample_rate_hz as u64)
262 + (source_sample_rate_hz as u64 / 2))
263 / source_sample_rate_hz as u64;
264 assert!(
265 destination_sample_count > 0,
266 "destination sample count must be > 0"
267 );
268 destination_sample_count as usize
269}
270
271#[inline]
272const fn sine_sample_from_phase(phase_u32: u32) -> i16 {
273 let half_cycle_u64 = 1_u64 << 31;
274 let one_q31_u64 = 1_u64 << 31;
275 let phase_u64 = phase_u32 as u64;
276 let (half_phase_u64, sign_i64) = if phase_u64 < half_cycle_u64 {
277 (phase_u64, 1_i64)
278 } else {
279 (phase_u64 - half_cycle_u64, -1_i64)
280 };
281
282 let product_q31_u64 = (half_phase_u64 * (one_q31_u64 - half_phase_u64)) >> 31;
285 let denominator_q31_u64 = 5 * one_q31_u64 - 4 * product_q31_u64;
286 let sine_q31_u64 = ((16 * product_q31_u64) << 31) / denominator_q31_u64;
287
288 let sample_i64 = (sine_q31_u64 as i64 * sign_i64) >> 16;
289 clamp_i64_to_i16(sample_i64)
290}
291
292#[doc(hidden)]
295#[inline]
296pub const fn scale_sample_with_linear(sample_i16: i16, linear_i32: i32) -> i16 {
297 if linear_i32 == 0 {
298 return 0;
299 }
300 let unity_scaled_linear_i64 = linear_i32 as i64 + 1;
303 let scaled_i64 = (sample_i16 as i64 * unity_scaled_linear_i64) / I16_ABS_MAX_I64;
304 clamp_i64_to_i16(scaled_i64)
305}
306
307#[doc(hidden)]
310#[inline]
311pub const fn scale_sample_with_volume(sample_i16: i16, volume: Volume) -> i16 {
312 scale_sample_with_linear(sample_i16, volume.to_i16() as i32)
313}
314
315#[inline]
316const fn scale_linear(linear_i32: i32, volume: Volume) -> i32 {
317 if volume.to_i16() == 0 || linear_i32 == 0 {
318 return 0;
319 }
320 let unity_scaled_volume_i64 = volume.to_i16() as i64 + 1;
321 ((linear_i32 as i64 * unity_scaled_volume_i64) / I16_ABS_MAX_I64) as i32
322}
323
324#[inline]
325const fn clamp_i64_to_i16(value_i64: i64) -> i16 {
326 if value_i64 > i16::MAX as i64 {
327 i16::MAX
328 } else if value_i64 < i16::MIN as i64 {
329 i16::MIN
330 } else {
331 value_i64 as i16
332 }
333}
334
335#[doc(hidden)]
337#[allow(async_fn_in_trait)]
338pub trait AudioOutputSink<const SAMPLE_BUFFER_LEN: usize> {
339 async fn write_stereo_words(
341 &mut self,
342 stereo_words: &[u32; SAMPLE_BUFFER_LEN],
343 stereo_word_count: usize,
344 ) -> Result<(), ()>;
345
346 async fn after_write(&mut self) {}
348}
349
350#[doc(hidden)]
352#[inline]
353pub const fn stereo_sample(sample: i16) -> u32 {
354 let sample_bits = sample as u16 as u32;
355 (sample_bits << 16) | sample_bits
356}
357
358#[doc(hidden)]
361pub async fn play_clip_sequence_once<
362 Output: AudioOutputSink<SAMPLE_BUFFER_LEN>,
363 const SAMPLE_BUFFER_LEN: usize,
364 const MAX_CLIPS: usize,
365 const SAMPLE_RATE_HZ: u32,
366>(
367 output: &mut Output,
368 audio_clips: &[PlaybackClip<SAMPLE_RATE_HZ>],
369 sample_buffer: &mut [u32; SAMPLE_BUFFER_LEN],
370 audio_player_static: &'static AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>,
371) -> Option<AudioCommand<MAX_CLIPS, SAMPLE_RATE_HZ>> {
372 for audio_clip in audio_clips {
373 match audio_clip {
374 PlaybackClip::Pcm(audio_clip) => {
375 if let ControlFlow::Break(next_audio_command) =
376 play_full_pcm_clip_once(output, audio_clip, sample_buffer, audio_player_static)
377 .await
378 {
379 return Some(next_audio_command);
380 }
381 }
382 PlaybackClip::Adpcm(adpcm_clip) => {
383 if let ControlFlow::Break(next_audio_command) = play_full_adpcm_clip_once(
384 output,
385 adpcm_clip,
386 sample_buffer,
387 audio_player_static,
388 )
389 .await
390 {
391 return Some(next_audio_command);
392 }
393 }
394 PlaybackClip::Silence(duration) => {
395 if let ControlFlow::Break(next_audio_command) = play_silence_duration_once(
396 output,
397 *duration,
398 sample_buffer,
399 audio_player_static,
400 )
401 .await
402 {
403 return Some(next_audio_command);
404 }
405 }
406 }
407 }
408 None
409}
410
411async fn play_full_pcm_clip_once<
412 Output: AudioOutputSink<SAMPLE_BUFFER_LEN>,
413 const SAMPLE_BUFFER_LEN: usize,
414 const MAX_CLIPS: usize,
415 const SAMPLE_RATE_HZ: u32,
416>(
417 output: &mut Output,
418 audio_clip: &PcmClip<SAMPLE_RATE_HZ>,
419 sample_buffer: &mut [u32; SAMPLE_BUFFER_LEN],
420 audio_player_static: &'static AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>,
421) -> ControlFlow<AudioCommand<MAX_CLIPS, SAMPLE_RATE_HZ>, ()> {
422 for audio_sample_chunk in audio_clip.samples().chunks(SAMPLE_BUFFER_LEN) {
423 let runtime_volume = audio_player_static.effective_runtime_volume();
424 for (sample_buffer_slot, sample_value_ref) in
425 sample_buffer.iter_mut().zip(audio_sample_chunk.iter())
426 {
427 let sample_value = *sample_value_ref;
428 let scaled_sample_value = scale_sample_with_volume(sample_value, runtime_volume);
429 *sample_buffer_slot = stereo_sample(scaled_sample_value);
430 }
431 sample_buffer[audio_sample_chunk.len()..].fill(stereo_sample(0));
432
433 if output
434 .write_stereo_words(sample_buffer, audio_sample_chunk.len())
435 .await
436 .is_err()
437 {
438 return ControlFlow::Continue(());
439 }
440 output.after_write().await;
441
442 if let Some(next_audio_command) = audio_player_static.try_take_command() {
443 return ControlFlow::Break(next_audio_command);
444 }
445 }
446
447 ControlFlow::Continue(())
448}
449
450async fn play_full_adpcm_clip_once<
451 Output: AudioOutputSink<SAMPLE_BUFFER_LEN>,
452 const SAMPLE_BUFFER_LEN: usize,
453 const MAX_CLIPS: usize,
454 const SAMPLE_RATE_HZ: u32,
455>(
456 output: &mut Output,
457 adpcm_clip: &AdpcmClip<SAMPLE_RATE_HZ>,
458 sample_buffer: &mut [u32; SAMPLE_BUFFER_LEN],
459 audio_player_static: &'static AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>,
460) -> ControlFlow<AudioCommand<MAX_CLIPS, SAMPLE_RATE_HZ>, ()> {
461 let mut sample_buffer_len = 0usize;
462 let mut remaining_pcm_sample_count = adpcm_clip.pcm_sample_count();
463 if remaining_pcm_sample_count == 0 {
464 return ControlFlow::Continue(());
465 }
466
467 let block_align = adpcm_clip.block_align() as usize;
468 for adpcm_block in adpcm_clip.data().chunks_exact(block_align) {
469 if remaining_pcm_sample_count == 0 {
470 break;
471 }
472 if adpcm_block.len() < 4 {
473 return ControlFlow::Continue(());
474 }
475
476 let runtime_volume = audio_player_static.effective_runtime_volume();
477 let mut predictor_i32 = match read_i16_le(adpcm_block, 0) {
478 Some(value) => value as i32,
479 None => return ControlFlow::Continue(()),
480 };
481 let mut step_index_i32 = adpcm_block[2] as i32;
482 if !(0..=88).contains(&step_index_i32) {
483 return ControlFlow::Continue(());
484 }
485
486 if remaining_pcm_sample_count > 0 {
487 sample_buffer[sample_buffer_len] = stereo_sample(scale_sample_with_volume(
488 predictor_i32 as i16,
489 runtime_volume,
490 ));
491 sample_buffer_len += 1;
492 remaining_pcm_sample_count -= 1;
493 if sample_buffer_len == SAMPLE_BUFFER_LEN {
494 if output
495 .write_stereo_words(sample_buffer, sample_buffer_len)
496 .await
497 .is_err()
498 {
499 return ControlFlow::Continue(());
500 }
501 output.after_write().await;
502 sample_buffer_len = 0;
503 if let Some(next_audio_command) = audio_player_static.try_take_command() {
504 return ControlFlow::Break(next_audio_command);
505 }
506 }
507 }
508
509 let mut samples_decoded_in_block = 1usize;
510 let samples_per_block = adpcm_clip.samples_per_block() as usize;
511
512 for adpcm_byte in &adpcm_block[4..] {
513 for adpcm_nibble in [adpcm_byte & 0x0F, adpcm_byte >> 4] {
514 if samples_decoded_in_block >= samples_per_block || remaining_pcm_sample_count == 0
515 {
516 break;
517 }
518
519 let decoded_sample_i16 = decode_adpcm_nibble_const(
520 adpcm_nibble,
521 &mut predictor_i32,
522 &mut step_index_i32,
523 );
524 sample_buffer[sample_buffer_len] =
525 stereo_sample(scale_sample_with_volume(decoded_sample_i16, runtime_volume));
526 sample_buffer_len += 1;
527 remaining_pcm_sample_count -= 1;
528 samples_decoded_in_block += 1;
529
530 if sample_buffer_len == SAMPLE_BUFFER_LEN {
531 if output
532 .write_stereo_words(sample_buffer, sample_buffer_len)
533 .await
534 .is_err()
535 {
536 return ControlFlow::Continue(());
537 }
538 output.after_write().await;
539 sample_buffer_len = 0;
540 if let Some(next_audio_command) = audio_player_static.try_take_command() {
541 return ControlFlow::Break(next_audio_command);
542 }
543 }
544 }
545 if remaining_pcm_sample_count == 0 {
546 break;
547 }
548 }
549
550 if let Some(next_audio_command) = audio_player_static.try_take_command() {
551 return ControlFlow::Break(next_audio_command);
552 }
553 }
554
555 if sample_buffer_len != 0 {
556 sample_buffer[sample_buffer_len..].fill(stereo_sample(0));
557 if output
558 .write_stereo_words(sample_buffer, sample_buffer_len)
559 .await
560 .is_err()
561 {
562 return ControlFlow::Continue(());
563 }
564 output.after_write().await;
565 if let Some(next_audio_command) = audio_player_static.try_take_command() {
566 return ControlFlow::Break(next_audio_command);
567 }
568 }
569
570 ControlFlow::Continue(())
571}
572
573async fn play_silence_duration_once<
574 Output: AudioOutputSink<SAMPLE_BUFFER_LEN>,
575 const SAMPLE_BUFFER_LEN: usize,
576 const MAX_CLIPS: usize,
577 const SAMPLE_RATE_HZ: u32,
578>(
579 output: &mut Output,
580 duration: Duration,
581 sample_buffer: &mut [u32; SAMPLE_BUFFER_LEN],
582 audio_player_static: &'static AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>,
583) -> ControlFlow<AudioCommand<MAX_CLIPS, SAMPLE_RATE_HZ>, ()> {
584 let silence_sample_count = __samples_for_duration(duration, SAMPLE_RATE_HZ);
585 let mut remaining_sample_count = silence_sample_count;
586 sample_buffer.fill(stereo_sample(0));
587
588 while remaining_sample_count > 0 {
589 let chunk_sample_count = remaining_sample_count.min(SAMPLE_BUFFER_LEN);
590 if output
591 .write_stereo_words(sample_buffer, chunk_sample_count)
592 .await
593 .is_err()
594 {
595 return ControlFlow::Continue(());
596 }
597 output.after_write().await;
598 remaining_sample_count -= chunk_sample_count;
599 if let Some(next_audio_command) = audio_player_static.try_take_command() {
600 return ControlFlow::Break(next_audio_command);
601 }
602 }
603
604 ControlFlow::Continue(())
605}
606
607#[inline]
608fn read_i16_le(bytes: &[u8], byte_offset: usize) -> Option<i16> {
609 let end_offset = byte_offset.checked_add(2)?;
610 if end_offset > bytes.len() {
611 return None;
612 }
613 Some(i16::from_le_bytes([
614 bytes[byte_offset],
615 bytes[byte_offset + 1],
616 ]))
617}
618
619pub enum AtEnd {
626 Loop,
628 Stop,
630}
631
632pub struct AdpcmClip<const SAMPLE_RATE_HZ: u32, T: ?Sized = [u8]> {
636 block_align: u16,
637 samples_per_block: u16,
638 pcm_sample_count: u32,
639 data: T,
640}
641
642pub type AdpcmClipBuf<const SAMPLE_RATE_HZ: u32, const DATA_LEN: usize> =
644 AdpcmClip<SAMPLE_RATE_HZ, [u8; DATA_LEN]>;
645
646impl<const SAMPLE_RATE_HZ: u32, T: ?Sized> AdpcmClip<SAMPLE_RATE_HZ, T> {
647 #[must_use]
649 pub fn block_align(&self) -> u16 {
650 self.block_align
651 }
652
653 #[must_use]
655 pub fn samples_per_block(&self) -> u16 {
656 self.samples_per_block
657 }
658
659 #[must_use]
661 pub fn pcm_sample_count(&self) -> usize {
662 self.pcm_sample_count as usize
663 }
664
665 #[must_use]
667 pub fn data(&self) -> &T {
668 &self.data
669 }
670}
671
672impl<const SAMPLE_RATE_HZ: u32, const DATA_LEN: usize> AdpcmClip<SAMPLE_RATE_HZ, [u8; DATA_LEN]> {
678 #[must_use]
680 pub(crate) const fn new(
681 block_align: u16,
682 samples_per_block: u16,
683 pcm_sample_count: usize,
684 data: [u8; DATA_LEN],
685 ) -> Self {
686 assert!(SAMPLE_RATE_HZ > 0, "sample_rate_hz must be > 0");
687 assert!(block_align >= 5, "block_align must be >= 5");
688 assert!(samples_per_block > 0, "samples_per_block must be > 0");
689 assert!(
690 DATA_LEN.is_multiple_of(block_align as usize),
691 "adpcm data length must be block aligned"
692 );
693 let max_decoded_sample_count =
694 (DATA_LEN / block_align as usize) * samples_per_block as usize;
695 assert!(
696 pcm_sample_count <= max_decoded_sample_count,
697 "pcm_sample_count must not exceed ADPCM block capacity"
698 );
699 assert!(
700 pcm_sample_count <= u32::MAX as usize,
701 "pcm_sample_count must fit in u32"
702 );
703 Self {
704 block_align,
705 samples_per_block,
706 pcm_sample_count: pcm_sample_count as u32,
707 data,
708 }
709 }
710
711 #[must_use]
717 pub const fn with_pcm<const SAMPLE_COUNT: usize>(
718 &self,
719 ) -> PcmClipBuf<SAMPLE_RATE_HZ, SAMPLE_COUNT> {
720 let block_align = self.block_align as usize;
721 assert!(block_align >= 5, "block_align must be >= 5");
722 assert!(
723 DATA_LEN.is_multiple_of(block_align),
724 "adpcm data length must be block aligned"
725 );
726
727 let samples_per_block = self.samples_per_block as usize;
728 assert!(samples_per_block > 0, "samples_per_block must be > 0");
729 let expected_sample_count = self.pcm_sample_count as usize;
730 assert!(
731 SAMPLE_COUNT == expected_sample_count,
732 "sample count must match decoded ADPCM length"
733 );
734
735 let mut samples = [0_i16; SAMPLE_COUNT];
736 if SAMPLE_COUNT == 0 {
737 assert!(SAMPLE_RATE_HZ > 0, "sample_rate_hz must be > 0");
738 return PcmClip { samples };
739 }
740
741 let mut sample_index = 0usize;
742 let mut remaining_sample_count = SAMPLE_COUNT;
743 let mut block_start = 0usize;
744 while block_start < DATA_LEN && remaining_sample_count > 0 {
746 let mut predictor_i32 = read_i16_le_const(&self.data, block_start) as i32;
747 let mut step_index_i32 = self.data[block_start + 2] as i32;
748 assert!(step_index_i32 >= 0, "ADPCM step_index must be >= 0");
749 assert!(step_index_i32 <= 88, "ADPCM step_index must be <= 88");
750
751 samples[sample_index] = predictor_i32 as i16;
752 sample_index += 1;
753 remaining_sample_count -= 1;
754 let mut decoded_in_block = 1usize;
755
756 let mut adpcm_byte_offset = block_start + 4;
757 let adpcm_block_end = block_start + block_align;
758 while adpcm_byte_offset < adpcm_block_end {
760 let adpcm_byte = self.data[adpcm_byte_offset];
761 let adpcm_nibble_low = adpcm_byte & 0x0F;
762 let adpcm_nibble_high = adpcm_byte >> 4;
763
764 if decoded_in_block < samples_per_block && remaining_sample_count > 0 {
765 samples[sample_index] = decode_adpcm_nibble_const(
766 adpcm_nibble_low,
767 &mut predictor_i32,
768 &mut step_index_i32,
769 );
770 sample_index += 1;
771 remaining_sample_count -= 1;
772 decoded_in_block += 1;
773 }
774 if decoded_in_block < samples_per_block && remaining_sample_count > 0 {
775 samples[sample_index] = decode_adpcm_nibble_const(
776 adpcm_nibble_high,
777 &mut predictor_i32,
778 &mut step_index_i32,
779 );
780 sample_index += 1;
781 remaining_sample_count -= 1;
782 decoded_in_block += 1;
783 }
784 if remaining_sample_count == 0 {
785 break;
786 }
787
788 adpcm_byte_offset += 1;
789 }
790
791 block_start += block_align;
792 }
793
794 assert!(SAMPLE_RATE_HZ > 0, "sample_rate_hz must be > 0");
795 PcmClip { samples }
796 }
797
798 #[must_use]
804 pub const fn with_gain(self, gain: Gain) -> Self {
805 let block_align = self.block_align as usize;
806 assert!(block_align >= 5, "block_align must be >= 5");
807 assert!(
808 DATA_LEN.is_multiple_of(block_align),
809 "adpcm data length must be block aligned"
810 );
811
812 let samples_per_block = self.samples_per_block as usize;
813 assert!(samples_per_block > 0, "samples_per_block must be > 0");
814 let max_samples_per_block = __adpcm_samples_per_block(block_align);
815 assert!(
816 samples_per_block <= max_samples_per_block,
817 "samples_per_block exceeds block_align capacity"
818 );
819
820 let mut gained_data = [0_u8; DATA_LEN];
821 let mut block_start = 0usize;
822 while block_start < DATA_LEN {
824 let mut source_predictor_i32 = read_i16_le_const(&self.data, block_start) as i32;
825 let mut source_step_index_i32 = self.data[block_start + 2] as i32;
826 assert!(
827 source_step_index_i32 >= 0 && source_step_index_i32 <= 88,
828 "ADPCM step_index must be in 0..=88"
829 );
830
831 let scaled_first_sample_i16 =
832 scale_sample_with_linear(source_predictor_i32 as i16, gain.linear());
833 let mut destination_predictor_i32 = scaled_first_sample_i16 as i32;
834 let mut destination_step_index_i32 = source_step_index_i32;
835
836 let scaled_first_sample_bytes = scaled_first_sample_i16.to_le_bytes();
837 gained_data[block_start] = scaled_first_sample_bytes[0];
838 gained_data[block_start + 1] = scaled_first_sample_bytes[1];
839 gained_data[block_start + 2] = destination_step_index_i32 as u8;
840 gained_data[block_start + 3] = 0;
841
842 let mut decoded_in_block = 1usize;
843 let mut source_byte_offset = block_start + 4;
844 let mut destination_byte_offset = block_start + 4;
845 let block_end = block_start + block_align;
846
847 while source_byte_offset < block_end {
849 let source_byte = self.data[source_byte_offset];
850 let mut destination_byte = 0_u8;
851
852 let mut nibble_index = 0usize;
853 while nibble_index < 2 {
855 if decoded_in_block < samples_per_block {
856 let source_nibble = if nibble_index == 0 {
857 source_byte & 0x0F
858 } else {
859 source_byte >> 4
860 };
861 let decoded_sample_i16 = decode_adpcm_nibble_const(
862 source_nibble,
863 &mut source_predictor_i32,
864 &mut source_step_index_i32,
865 );
866 let scaled_sample_i32 =
867 scale_sample_with_linear(decoded_sample_i16, gain.linear()) as i32;
868 let destination_nibble = encode_adpcm_nibble(
869 scaled_sample_i32,
870 &mut destination_predictor_i32,
871 &mut destination_step_index_i32,
872 );
873 destination_byte |= destination_nibble << (nibble_index * 4);
874 decoded_in_block += 1;
875 }
876 nibble_index += 1;
877 }
878
879 gained_data[destination_byte_offset] = destination_byte;
880 source_byte_offset += 1;
881 destination_byte_offset += 1;
882 }
883
884 block_start += block_align;
885 }
886
887 Self::new(
888 self.block_align,
889 self.samples_per_block,
890 self.pcm_sample_count as usize,
891 gained_data,
892 )
893 }
894}
895
896#[derive(Clone, Copy)]
898#[doc(hidden)]
899pub struct ParsedAdpcmWavHeader {
900 pub sample_rate_hz: u32,
902 pub block_align: usize,
904 pub samples_per_block: usize,
906 pub data_chunk_start: usize,
908 pub data_chunk_len: usize,
910 pub sample_count: usize,
912}
913
914#[must_use]
916#[doc(hidden)]
917pub const fn __parse_adpcm_wav_header(wav_bytes: &[u8]) -> ParsedAdpcmWavHeader {
918 if wav_bytes.len() < 12 {
919 panic!("WAV file too small");
920 }
921 if !wav_tag_eq(wav_bytes, 0, *b"RIFF") {
922 panic!("Missing RIFF header");
923 }
924 if !wav_tag_eq(wav_bytes, 8, *b"WAVE") {
925 panic!("Missing WAVE header");
926 }
927
928 let mut chunk_offset = 12usize;
929 let mut sample_rate_hz = 0u32;
930 let mut block_align = 0usize;
931 let mut samples_per_block = 0usize;
932 let mut fmt_found = false;
933 let mut data_chunk_start = 0usize;
934 let mut data_chunk_end = 0usize;
935 let mut data_found = false;
936
937 while chunk_offset + 8 <= wav_bytes.len() {
939 let chunk_size = read_u32_le_const(wav_bytes, chunk_offset + 4) as usize;
940 let chunk_data_start = chunk_offset + 8;
941 if chunk_data_start > wav_bytes.len() || chunk_size > wav_bytes.len() - chunk_data_start {
942 panic!("WAV chunk overruns file");
943 }
944 let chunk_data_end = chunk_data_start + chunk_size;
945
946 if wav_tag_eq(wav_bytes, chunk_offset, *b"fmt ") {
947 if chunk_size < 16 {
948 panic!("fmt chunk too small");
949 }
950
951 let audio_format = read_u16_le_const(wav_bytes, chunk_data_start);
952 let channels = read_u16_le_const(wav_bytes, chunk_data_start + 2);
953 sample_rate_hz = read_u32_le_const(wav_bytes, chunk_data_start + 4);
954 block_align = read_u16_le_const(wav_bytes, chunk_data_start + 12) as usize;
955 let bits_per_sample = read_u16_le_const(wav_bytes, chunk_data_start + 14);
956
957 if audio_format != 0x0011 {
958 panic!("Expected ADPCM WAV format");
959 }
960 if channels != 1 {
961 panic!("Expected mono ADPCM WAV");
962 }
963 if bits_per_sample != 4 {
964 panic!("Expected 4-bit ADPCM");
965 }
966 if block_align < 5 {
967 panic!("ADPCM block_align too small");
968 }
969
970 let derived_samples_per_block = derive_samples_per_block_const(block_align);
971 samples_per_block = if chunk_size >= 22 {
972 read_u16_le_const(wav_bytes, chunk_data_start + 18) as usize
973 } else {
974 derived_samples_per_block
975 };
976 if samples_per_block != derived_samples_per_block {
977 panic!("Unexpected ADPCM samples_per_block");
978 }
979 fmt_found = true;
980 } else if wav_tag_eq(wav_bytes, chunk_offset, *b"data") {
981 data_chunk_start = chunk_data_start;
982 data_chunk_end = chunk_data_end;
983 data_found = true;
984 }
985
986 let padded_chunk_size = chunk_size + (chunk_size & 1);
987 if chunk_data_start > usize::MAX - padded_chunk_size {
988 panic!("WAV chunk traversal overflow");
989 }
990 chunk_offset = chunk_data_start + padded_chunk_size;
991 }
992
993 if !fmt_found {
994 panic!("Missing fmt chunk");
995 }
996 if !data_found {
997 panic!("Missing data chunk");
998 }
999 let data_chunk_len = data_chunk_end - data_chunk_start;
1000 if !data_chunk_len.is_multiple_of(block_align) {
1001 panic!("data chunk is not block aligned");
1002 }
1003
1004 ParsedAdpcmWavHeader {
1005 sample_rate_hz,
1006 block_align,
1007 samples_per_block,
1008 data_chunk_start,
1009 data_chunk_len,
1010 sample_count: (data_chunk_len / block_align) * samples_per_block,
1011 }
1012}
1013
1014const fn wav_tag_eq(wav_bytes: &[u8], byte_offset: usize, tag_bytes: [u8; 4]) -> bool {
1015 if byte_offset > wav_bytes.len().saturating_sub(4) {
1016 return false;
1017 }
1018 wav_bytes[byte_offset] == tag_bytes[0]
1019 && wav_bytes[byte_offset + 1] == tag_bytes[1]
1020 && wav_bytes[byte_offset + 2] == tag_bytes[2]
1021 && wav_bytes[byte_offset + 3] == tag_bytes[3]
1022}
1023
1024const fn derive_samples_per_block_const(block_align: usize) -> usize {
1025 if block_align < 4 {
1026 panic!("ADPCM block_align underflow");
1027 }
1028 ((block_align - 4) * 2) + 1
1029}
1030
1031const ADPCM_INDEX_TABLE: [i32; 16] = [-1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8];
1032const ADPCM_STEP_TABLE: [i32; 89] = [
1033 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66,
1034 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449,
1035 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272,
1036 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493,
1037 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767,
1038];
1039
1040#[doc(hidden)]
1044#[must_use]
1045pub const fn __adpcm_samples_per_block(block_align: usize) -> usize {
1046 if block_align < 5 {
1047 panic!("block_align must be >= 5 for ADPCM");
1048 }
1049 derive_samples_per_block_const(block_align)
1050}
1051
1052#[doc(hidden)]
1054#[must_use]
1055pub const fn __adpcm_data_len_for_pcm_samples(sample_count: usize) -> usize {
1056 __adpcm_data_len_for_pcm_samples_with_block_align(sample_count, ADPCM_ENCODE_BLOCK_ALIGN)
1057}
1058
1059#[doc(hidden)]
1062#[must_use]
1063pub const fn __adpcm_data_len_for_pcm_samples_with_block_align(
1064 sample_count: usize,
1065 block_align: usize,
1066) -> usize {
1067 let samples_per_block = __adpcm_samples_per_block(block_align);
1068 let block_count = if sample_count == 0 {
1069 0
1070 } else {
1071 ((sample_count - 1) / samples_per_block) + 1
1072 };
1073 block_count * block_align
1074}
1075
1076const fn read_u16_le_const(bytes: &[u8], byte_offset: usize) -> u16 {
1077 if byte_offset > bytes.len().saturating_sub(2) {
1078 panic!("read_u16_le_const out of bounds");
1079 }
1080 u16::from_le_bytes([bytes[byte_offset], bytes[byte_offset + 1]])
1081}
1082
1083const fn read_i16_le_const(bytes: &[u8], byte_offset: usize) -> i16 {
1084 if byte_offset > bytes.len().saturating_sub(2) {
1085 panic!("read_i16_le_const out of bounds");
1086 }
1087 i16::from_le_bytes([bytes[byte_offset], bytes[byte_offset + 1]])
1088}
1089
1090const fn read_u32_le_const(bytes: &[u8], byte_offset: usize) -> u32 {
1091 if byte_offset > bytes.len().saturating_sub(4) {
1092 panic!("read_u32_le_const out of bounds");
1093 }
1094 u32::from_le_bytes([
1095 bytes[byte_offset],
1096 bytes[byte_offset + 1],
1097 bytes[byte_offset + 2],
1098 bytes[byte_offset + 3],
1099 ])
1100}
1101
1102#[doc(hidden)]
1105pub enum PlaybackClip<const SAMPLE_RATE_HZ: u32> {
1106 Pcm(&'static PcmClip<SAMPLE_RATE_HZ>),
1107 Adpcm(&'static AdpcmClip<SAMPLE_RATE_HZ>),
1108 Silence(Duration),
1109}
1110
1111#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1119pub struct SilenceClip {
1120 duration: Duration,
1121}
1122
1123impl SilenceClip {
1124 #[must_use]
1127 pub const fn new(duration: core::time::Duration) -> Self {
1128 Self { duration }
1129 }
1130
1131 #[must_use]
1134 pub const fn duration(self) -> core::time::Duration {
1135 self.duration
1136 }
1137}
1138
1139#[allow(private_bounds)]
1149pub trait Playable<const SAMPLE_RATE_HZ: u32>: sealed::PlayableSealed<SAMPLE_RATE_HZ> {}
1150
1151impl<const SAMPLE_RATE_HZ: u32, T: ?Sized> Playable<SAMPLE_RATE_HZ> for T where
1152 T: sealed::PlayableSealed<SAMPLE_RATE_HZ>
1153{
1154}
1155
1156#[allow(async_fn_in_trait)]
1384pub trait AudioPlayer<const SAMPLE_RATE_HZ: u32> {
1385 const SAMPLE_RATE_HZ: u32;
1387 const MAX_CLIPS: usize;
1389 const INITIAL_VOLUME: Volume;
1391 const MAX_VOLUME: Volume;
1393
1394 fn play<I>(&self, audio_clips: I, at_end: AtEnd)
1401 where
1402 I: IntoIterator<Item = &'static dyn Playable<SAMPLE_RATE_HZ>>;
1403
1404 fn stop(&self);
1408
1409 async fn wait_until_stopped(&self);
1413
1414 fn set_volume(&self, volume: Volume);
1418
1419 fn volume(&self) -> Volume;
1423}
1424
1425mod sealed {
1426 use super::{AdpcmClip, PcmClip, PlaybackClip, SilenceClip};
1427
1428 pub(crate) trait PlayableSealed<const SAMPLE_RATE_HZ: u32> {
1429 fn playback_clip(&'static self) -> PlaybackClip<SAMPLE_RATE_HZ>;
1430 }
1431
1432 impl<const SAMPLE_RATE_HZ: u32> PlayableSealed<SAMPLE_RATE_HZ> for PcmClip<SAMPLE_RATE_HZ> {
1433 fn playback_clip(&'static self) -> PlaybackClip<SAMPLE_RATE_HZ> {
1434 PlaybackClip::Pcm(self)
1435 }
1436 }
1437
1438 impl<const SAMPLE_RATE_HZ: u32, const SAMPLE_COUNT: usize> PlayableSealed<SAMPLE_RATE_HZ>
1439 for PcmClip<SAMPLE_RATE_HZ, [i16; SAMPLE_COUNT]>
1440 {
1441 fn playback_clip(&'static self) -> PlaybackClip<SAMPLE_RATE_HZ> {
1442 PlaybackClip::Pcm(self)
1443 }
1444 }
1445
1446 impl<const SAMPLE_RATE_HZ: u32> PlayableSealed<SAMPLE_RATE_HZ> for AdpcmClip<SAMPLE_RATE_HZ> {
1447 fn playback_clip(&'static self) -> PlaybackClip<SAMPLE_RATE_HZ> {
1448 PlaybackClip::Adpcm(self)
1449 }
1450 }
1451
1452 impl<const SAMPLE_RATE_HZ: u32, const DATA_LEN: usize> PlayableSealed<SAMPLE_RATE_HZ>
1453 for AdpcmClip<SAMPLE_RATE_HZ, [u8; DATA_LEN]>
1454 {
1455 fn playback_clip(&'static self) -> PlaybackClip<SAMPLE_RATE_HZ> {
1456 PlaybackClip::Adpcm(self)
1457 }
1458 }
1459
1460 impl<const SAMPLE_RATE_HZ: u32> PlayableSealed<SAMPLE_RATE_HZ> for SilenceClip {
1461 fn playback_clip(&'static self) -> PlaybackClip<SAMPLE_RATE_HZ> {
1462 PlaybackClip::Silence(self.duration())
1463 }
1464 }
1465}
1466
1467pub struct PcmClip<const SAMPLE_RATE_HZ: u32, T: ?Sized = [i16]> {
1474 samples: T,
1475}
1476
1477impl<const SAMPLE_RATE_HZ: u32, T: ?Sized> PcmClip<SAMPLE_RATE_HZ, T> {
1478 #[must_use]
1480 pub fn samples(&self) -> &T {
1481 &self.samples
1482 }
1483}
1484
1485pub type PcmClipBuf<const SAMPLE_RATE_HZ: u32, const SAMPLE_COUNT: usize> =
1495 PcmClip<SAMPLE_RATE_HZ, [i16; SAMPLE_COUNT]>;
1496
1497impl<const SAMPLE_RATE_HZ: u32, const SAMPLE_COUNT: usize>
1503 PcmClip<SAMPLE_RATE_HZ, [i16; SAMPLE_COUNT]>
1504{
1505 #[must_use]
1516 pub const fn with_gain(self, gain: Gain) -> Self {
1517 assert!(SAMPLE_RATE_HZ > 0, "sample_rate_hz must be > 0");
1518 let mut scaled_samples = [0_i16; SAMPLE_COUNT];
1519 let mut sample_index = 0_usize;
1520 while sample_index < SAMPLE_COUNT {
1522 scaled_samples[sample_index] =
1523 scale_sample_with_linear(self.samples[sample_index], gain.linear());
1524 sample_index += 1;
1525 }
1526 Self {
1527 samples: scaled_samples,
1528 }
1529 }
1530
1531 #[must_use]
1539 pub(crate) const fn with_attack_release(self, attack: Duration, release: Duration) -> Self {
1540 assert!(SAMPLE_RATE_HZ > 0, "sample_rate_hz must be > 0");
1541 let attack_sample_count = __samples_for_duration(attack, SAMPLE_RATE_HZ);
1542 let release_sample_count = __samples_for_duration(release, SAMPLE_RATE_HZ);
1543 self.with_attack_release_sample_count(attack_sample_count, release_sample_count)
1544 }
1545
1546 #[must_use]
1547 const fn with_attack_release_sample_count(
1548 self,
1549 attack_sample_count: usize,
1550 release_sample_count: usize,
1551 ) -> Self {
1552 assert!(
1553 attack_sample_count <= SAMPLE_COUNT,
1554 "attack duration must fit within clip duration"
1555 );
1556 assert!(
1557 release_sample_count <= SAMPLE_COUNT,
1558 "release duration must fit within clip duration"
1559 );
1560 assert!(
1561 attack_sample_count + release_sample_count <= SAMPLE_COUNT,
1562 "attack + release must fit within clip duration"
1563 );
1564
1565 let mut shaped_samples = self.samples;
1566
1567 if attack_sample_count > 0 {
1568 let attack_sample_count_i32 = attack_sample_count as i32;
1569 let mut sample_index = 0usize;
1570 while sample_index < attack_sample_count {
1572 let envelope_numerator_i32 = sample_index as i32;
1573 shaped_samples[sample_index] = scale_sample_with_linear(
1574 shaped_samples[sample_index],
1575 (envelope_numerator_i32 * i16::MAX as i32) / attack_sample_count_i32,
1576 );
1577 sample_index += 1;
1578 }
1579 }
1580
1581 if release_sample_count > 0 {
1582 let release_sample_count_i32 = release_sample_count as i32;
1583 let release_start_index = SAMPLE_COUNT - release_sample_count;
1584 let mut release_index = 0usize;
1585 while release_index < release_sample_count {
1587 let sample_index = release_start_index + release_index;
1588 let envelope_numerator_i32 = (release_sample_count - release_index) as i32;
1589 shaped_samples[sample_index] = scale_sample_with_linear(
1590 shaped_samples[sample_index],
1591 (envelope_numerator_i32 * i16::MAX as i32) / release_sample_count_i32,
1592 );
1593 release_index += 1;
1594 }
1595 }
1596
1597 Self {
1598 samples: shaped_samples,
1599 }
1600 }
1601
1602 #[must_use]
1607 pub const fn with_adpcm<const DATA_LEN: usize>(
1608 &self,
1609 ) -> AdpcmClipBuf<SAMPLE_RATE_HZ, DATA_LEN> {
1610 self.with_adpcm_block_align::<DATA_LEN>(ADPCM_ENCODE_BLOCK_ALIGN)
1611 }
1612
1613 #[must_use]
1614 pub(crate) const fn with_adpcm_block_align<const DATA_LEN: usize>(
1615 &self,
1616 block_align: usize,
1617 ) -> AdpcmClipBuf<SAMPLE_RATE_HZ, DATA_LEN> {
1618 assert!(block_align >= 5, "block_align must be >= 5");
1619 assert!(
1620 block_align <= u16::MAX as usize,
1621 "block_align must fit in u16"
1622 );
1623 let samples_per_block = __adpcm_samples_per_block(block_align);
1624 assert!(
1625 samples_per_block <= u16::MAX as usize,
1626 "samples_per_block must fit in u16"
1627 );
1628 assert!(
1629 DATA_LEN
1630 == __adpcm_data_len_for_pcm_samples_with_block_align(SAMPLE_COUNT, block_align),
1631 "adpcm data length must match sample count and block_align"
1632 );
1633 if SAMPLE_COUNT == 0 {
1634 return AdpcmClip::new(
1635 block_align as u16,
1636 samples_per_block as u16,
1637 SAMPLE_COUNT,
1638 [0; DATA_LEN],
1639 );
1640 }
1641
1642 let mut adpcm_data = [0_u8; DATA_LEN];
1643 let mut sample_index = 0usize;
1644 let mut data_index = 0usize;
1645 let payload_len_per_block = block_align - 4;
1646
1647 while sample_index < SAMPLE_COUNT {
1649 let mut predictor_i32 = self.samples[sample_index] as i32;
1650 let mut step_index_i32 = 0_i32;
1651
1652 let predictor_i16 = predictor_i32 as i16;
1653 let predictor_bytes = predictor_i16.to_le_bytes();
1654 adpcm_data[data_index] = predictor_bytes[0];
1655 adpcm_data[data_index + 1] = predictor_bytes[1];
1656 adpcm_data[data_index + 2] = step_index_i32 as u8;
1657 adpcm_data[data_index + 3] = 0;
1658 data_index += 4;
1659 sample_index += 1;
1660
1661 let mut payload_byte_index = 0usize;
1662 while payload_byte_index < payload_len_per_block {
1664 let mut adpcm_byte = 0_u8;
1665
1666 let mut nibble_index = 0usize;
1667 while nibble_index < 2 {
1669 let target_sample_i32 = if sample_index < SAMPLE_COUNT {
1670 self.samples[sample_index] as i32
1671 } else {
1672 predictor_i32
1673 };
1674 let adpcm_nibble = encode_adpcm_nibble(
1675 target_sample_i32,
1676 &mut predictor_i32,
1677 &mut step_index_i32,
1678 );
1679 adpcm_byte |= adpcm_nibble << (nibble_index * 4);
1680 sample_index += 1;
1681 nibble_index += 1;
1682 }
1683
1684 adpcm_data[data_index] = adpcm_byte;
1685 data_index += 1;
1686 payload_byte_index += 1;
1687 }
1688 }
1689
1690 AdpcmClip::new(
1691 block_align as u16,
1692 samples_per_block as u16,
1693 SAMPLE_COUNT,
1694 adpcm_data,
1695 )
1696 }
1697}
1698
1699#[doc(hidden)]
1702pub enum AudioCommand<const MAX_CLIPS: usize, const SAMPLE_RATE_HZ: u32> {
1703 Play {
1704 audio_clips: Vec<PlaybackClip<SAMPLE_RATE_HZ>, MAX_CLIPS>,
1705 at_end: AtEnd,
1706 },
1707 Stop,
1708}
1709
1710#[doc(hidden)]
1713pub struct AudioPlayerStatic<const MAX_CLIPS: usize, const SAMPLE_RATE_HZ: u32> {
1714 command_signal: Signal<CriticalSectionRawMutex, AudioCommand<MAX_CLIPS, SAMPLE_RATE_HZ>>,
1715 stopped_signal: Signal<CriticalSectionRawMutex, ()>,
1716 is_playing: AtomicBool,
1717 has_pending_play: AtomicBool,
1718 max_volume_linear: i32,
1719 runtime_volume_relative_linear: AtomicI32,
1720}
1721
1722impl<const MAX_CLIPS: usize, const SAMPLE_RATE_HZ: u32>
1723 AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>
1724{
1725 #[must_use]
1727 pub const fn new_static() -> Self {
1728 Self::new_static_with_max_volume_and_initial_volume(Volume::MAX, Volume::MAX)
1729 }
1730
1731 #[must_use]
1733 pub const fn new_static_with_max_volume(max_volume: Volume) -> Self {
1734 Self::new_static_with_max_volume_and_initial_volume(max_volume, Volume::MAX)
1735 }
1736
1737 #[must_use]
1740 pub const fn new_static_with_max_volume_and_initial_volume(
1741 max_volume: Volume,
1742 initial_volume: Volume,
1743 ) -> Self {
1744 Self {
1745 command_signal: Signal::new(),
1746 stopped_signal: Signal::new(),
1747 is_playing: AtomicBool::new(false),
1748 has_pending_play: AtomicBool::new(false),
1749 max_volume_linear: max_volume.to_i16() as i32,
1750 runtime_volume_relative_linear: AtomicI32::new(initial_volume.to_i16() as i32),
1751 }
1752 }
1753
1754 fn signal(&self, audio_command: AudioCommand<MAX_CLIPS, SAMPLE_RATE_HZ>) {
1755 self.command_signal.signal(audio_command);
1756 }
1757
1758 fn mark_pending_play(&self) {
1759 self.has_pending_play.store(true, AtomicOrdering::Relaxed);
1760 }
1761
1762 #[doc(hidden)]
1764 pub fn mark_playing(&self) {
1765 self.has_pending_play.store(false, AtomicOrdering::Relaxed);
1766 self.is_playing.store(true, AtomicOrdering::Relaxed);
1767 }
1768
1769 #[doc(hidden)]
1771 pub fn mark_stopped(&self) {
1772 self.has_pending_play.store(false, AtomicOrdering::Relaxed);
1773 self.is_playing.store(false, AtomicOrdering::Relaxed);
1774 self.stopped_signal.signal(());
1775 }
1776
1777 fn is_idle(&self) -> bool {
1778 !self.has_pending_play.load(AtomicOrdering::Relaxed)
1779 && !self.is_playing.load(AtomicOrdering::Relaxed)
1780 }
1781
1782 async fn wait_until_stopped(&self) {
1783 while !self.is_idle() {
1784 self.stopped_signal.wait().await;
1785 }
1786 }
1787
1788 fn set_runtime_volume(&self, volume: Volume) {
1789 self.runtime_volume_relative_linear
1790 .store(volume.to_i16() as i32, Ordering::Relaxed);
1791 }
1792
1793 fn runtime_volume(&self) -> Volume {
1794 Volume::from_i16(self.runtime_volume_relative_linear.load(Ordering::Relaxed) as i16)
1795 }
1796
1797 #[doc(hidden)]
1800 pub fn effective_runtime_volume(&self) -> Volume {
1801 let runtime_volume_relative = self.runtime_volume();
1802 Volume::from_i16(scale_linear(self.max_volume_linear, runtime_volume_relative) as i16)
1803 }
1804
1805 #[doc(hidden)]
1807 pub async fn wait(&self) -> AudioCommand<MAX_CLIPS, SAMPLE_RATE_HZ> {
1808 self.command_signal.wait().await
1809 }
1810
1811 #[doc(hidden)]
1814 pub fn try_take_command(&self) -> Option<AudioCommand<MAX_CLIPS, SAMPLE_RATE_HZ>> {
1815 self.command_signal.try_take()
1816 }
1817}
1818
1819#[doc(hidden)]
1821pub fn __audio_player_play<I, const MAX_CLIPS: usize, const SAMPLE_RATE_HZ: u32>(
1822 audio_player_static: &'static AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>,
1823 audio_clips: I,
1824 at_end: AtEnd,
1825) where
1826 I: IntoIterator<Item = &'static dyn Playable<SAMPLE_RATE_HZ>>,
1827{
1828 assert!(MAX_CLIPS > 0, "play disabled: max_clips is 0");
1829 let mut audio_clip_sequence: Vec<PlaybackClip<SAMPLE_RATE_HZ>, MAX_CLIPS> = Vec::new();
1830 for audio_clip in audio_clips {
1831 assert!(
1832 audio_clip_sequence
1833 .push(sealed::PlayableSealed::playback_clip(audio_clip))
1834 .is_ok(),
1835 "play sequence fits within max_clips"
1836 );
1837 }
1838 assert!(
1839 !audio_clip_sequence.is_empty(),
1840 "play requires at least one clip"
1841 );
1842
1843 audio_player_static.mark_pending_play();
1844 audio_player_static.signal(AudioCommand::Play {
1845 audio_clips: audio_clip_sequence,
1846 at_end,
1847 });
1848}
1849
1850#[doc(hidden)]
1852pub fn __audio_player_stop<const MAX_CLIPS: usize, const SAMPLE_RATE_HZ: u32>(
1853 audio_player_static: &'static AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>,
1854) {
1855 audio_player_static.signal(AudioCommand::Stop);
1856}
1857
1858#[doc(hidden)]
1860pub async fn __audio_player_wait_until_stopped<
1861 const MAX_CLIPS: usize,
1862 const SAMPLE_RATE_HZ: u32,
1863>(
1864 audio_player_static: &'static AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>,
1865) {
1866 audio_player_static.wait_until_stopped().await;
1867}
1868
1869#[doc(hidden)]
1871pub fn __audio_player_set_volume<const MAX_CLIPS: usize, const SAMPLE_RATE_HZ: u32>(
1872 audio_player_static: &'static AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>,
1873 volume: Volume,
1874) {
1875 audio_player_static.set_runtime_volume(volume);
1876}
1877
1878#[doc(hidden)]
1880#[must_use]
1881pub fn __audio_player_volume<const MAX_CLIPS: usize, const SAMPLE_RATE_HZ: u32>(
1882 audio_player_static: &'static AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>,
1883) -> Volume {
1884 audio_player_static.runtime_volume()
1885}
1886
1887#[must_use]
1892#[doc(hidden)]
1893pub const fn __tone_pcm_clip<const SAMPLE_RATE_HZ: u32, const SAMPLE_COUNT: usize>(
1894 frequency_hz: u32,
1895) -> PcmClipBuf<SAMPLE_RATE_HZ, SAMPLE_COUNT> {
1896 __tone_pcm_clip_with_duration::<SAMPLE_RATE_HZ, SAMPLE_COUNT>(
1897 frequency_hz,
1898 duration_for_sample_count(SAMPLE_COUNT, SAMPLE_RATE_HZ),
1899 )
1900}
1901
1902#[must_use]
1906#[doc(hidden)]
1907pub const fn __tone_pcm_clip_with_duration<const SAMPLE_RATE_HZ: u32, const SAMPLE_COUNT: usize>(
1908 frequency_hz: u32,
1909 duration: core::time::Duration,
1910) -> PcmClipBuf<SAMPLE_RATE_HZ, SAMPLE_COUNT> {
1911 assert!(SAMPLE_RATE_HZ > 0, "sample_rate_hz must be > 0");
1912 let mut samples = [0_i16; SAMPLE_COUNT];
1913 let phase_step_u64 = ((frequency_hz as u64) << 32) / SAMPLE_RATE_HZ as u64;
1914 let phase_step_u32 = phase_step_u64 as u32;
1915 let mut phase_u32 = 0_u32;
1916
1917 let mut sample_index = 0usize;
1918 while sample_index < SAMPLE_COUNT {
1920 samples[sample_index] = sine_sample_from_phase(phase_u32);
1921 phase_u32 = phase_u32.wrapping_add(phase_step_u32);
1922 sample_index += 1;
1923 }
1924
1925 let max_duration = Duration::from_millis(50);
1927 assert!(max_duration.as_secs() == 0, "50ms cap must be sub-second");
1928 let attack_release_duration = match (duration.as_secs(), duration.subsec_nanos()) {
1929 (0, nanos) if nanos / 4 < max_duration.subsec_nanos() => Duration::new(0, nanos / 4),
1930 (_, _) => max_duration,
1931 };
1932 PcmClip { samples }.with_attack_release(attack_release_duration, attack_release_duration)
1933}
1934
1935#[must_use]
1940#[doc(hidden)]
1941pub const fn __pcm_clip_from_samples<const SAMPLE_RATE_HZ: u32, const SAMPLE_COUNT: usize>(
1942 samples: [i16; SAMPLE_COUNT],
1943) -> PcmClipBuf<SAMPLE_RATE_HZ, SAMPLE_COUNT> {
1944 assert!(SAMPLE_RATE_HZ > 0, "sample_rate_hz must be > 0");
1945 PcmClip { samples }
1946}
1947
1948#[must_use]
1953#[doc(hidden)]
1954pub const fn __adpcm_clip_from_parts<const SAMPLE_RATE_HZ: u32, const DATA_LEN: usize>(
1955 block_align: u16,
1956 samples_per_block: u16,
1957 pcm_sample_count: usize,
1958 data: [u8; DATA_LEN],
1959) -> AdpcmClipBuf<SAMPLE_RATE_HZ, DATA_LEN> {
1960 AdpcmClip::new(block_align, samples_per_block, pcm_sample_count, data)
1961}
1962
1963#[must_use]
1968#[doc(hidden)]
1969pub const fn __pcm_with_adpcm_block_align<
1970 const SAMPLE_RATE_HZ: u32,
1971 const SAMPLE_COUNT: usize,
1972 const DATA_LEN: usize,
1973>(
1974 source_pcm_clip: &PcmClipBuf<SAMPLE_RATE_HZ, SAMPLE_COUNT>,
1975 block_align: usize,
1976) -> AdpcmClipBuf<SAMPLE_RATE_HZ, DATA_LEN> {
1977 source_pcm_clip.with_adpcm_block_align::<DATA_LEN>(block_align)
1978}
1979
1980#[must_use]
1986#[doc(hidden)]
1987pub const fn __resample_pcm_clip<
1988 const SOURCE_HZ: u32,
1989 const SOURCE_COUNT: usize,
1990 const TARGET_HZ: u32,
1991 const TARGET_COUNT: usize,
1992>(
1993 source_pcm_clip: PcmClipBuf<SOURCE_HZ, SOURCE_COUNT>,
1994) -> PcmClipBuf<TARGET_HZ, TARGET_COUNT> {
1995 assert!(SOURCE_COUNT > 0, "source sample count must be > 0");
1996 assert!(TARGET_HZ > 0, "destination sample_rate_hz must be > 0");
1997 let expected_destination_sample_count =
1998 __resampled_sample_count(SOURCE_COUNT, SOURCE_HZ, TARGET_HZ);
1999 assert!(
2000 TARGET_COUNT == expected_destination_sample_count,
2001 "destination sample count must preserve duration"
2002 );
2003
2004 let source_samples = source_pcm_clip.samples;
2005 let mut resampled_samples = [0_i16; TARGET_COUNT];
2006 let mut sample_index = 0_usize;
2007
2008 while sample_index < TARGET_COUNT {
2010 let source_position_numerator_u128 = sample_index as u128 * SOURCE_HZ as u128;
2011 let source_index_u128 = source_position_numerator_u128 / TARGET_HZ as u128;
2012 let source_fraction_numerator_u128 = source_position_numerator_u128 % TARGET_HZ as u128;
2013 let source_index = source_index_u128 as usize;
2014
2015 resampled_samples[sample_index] = if source_index + 1 >= SOURCE_COUNT {
2016 source_samples[SOURCE_COUNT - 1]
2017 } else if source_fraction_numerator_u128 == 0 {
2018 source_samples[source_index]
2019 } else {
2020 let left_sample_i128 = source_samples[source_index] as i128;
2021 let right_sample_i128 = source_samples[source_index + 1] as i128;
2022 let sample_delta_i128 = right_sample_i128 - left_sample_i128;
2023 let denom_i128 = TARGET_HZ as i128;
2024 let numerator_i128 = sample_delta_i128 * source_fraction_numerator_u128 as i128;
2025 let rounded_i128 = if numerator_i128 >= 0 {
2026 (numerator_i128 + (denom_i128 / 2)) / denom_i128
2027 } else {
2028 (numerator_i128 - (denom_i128 / 2)) / denom_i128
2029 };
2030 clamp_i64_to_i16((left_sample_i128 + rounded_i128) as i64)
2031 };
2032
2033 sample_index += 1;
2034 }
2035
2036 PcmClip {
2037 samples: resampled_samples,
2038 }
2039}
2040
2041#[doc(hidden)]
2044pub const fn decode_adpcm_nibble_const(
2045 adpcm_nibble: u8,
2046 predictor_i32: &mut i32,
2047 step_index_i32: &mut i32,
2048) -> i16 {
2049 let step = ADPCM_STEP_TABLE[*step_index_i32 as usize];
2050 let mut delta = step >> 3;
2051
2052 if (adpcm_nibble & 0x01) != 0 {
2053 delta += step >> 2;
2054 }
2055 if (adpcm_nibble & 0x02) != 0 {
2056 delta += step >> 1;
2057 }
2058 if (adpcm_nibble & 0x04) != 0 {
2059 delta += step;
2060 }
2061
2062 if (adpcm_nibble & 0x08) != 0 {
2063 *predictor_i32 -= delta;
2064 } else {
2065 *predictor_i32 += delta;
2066 }
2067
2068 if *predictor_i32 < i16::MIN as i32 {
2069 *predictor_i32 = i16::MIN as i32;
2070 } else if *predictor_i32 > i16::MAX as i32 {
2071 *predictor_i32 = i16::MAX as i32;
2072 }
2073 *step_index_i32 += ADPCM_INDEX_TABLE[adpcm_nibble as usize];
2074 if *step_index_i32 < 0 {
2075 *step_index_i32 = 0;
2076 } else if *step_index_i32 > 88 {
2077 *step_index_i32 = 88;
2078 }
2079
2080 *predictor_i32 as i16
2081}
2082
2083const fn encode_adpcm_nibble(
2084 target_sample_i32: i32,
2085 predictor_i32: &mut i32,
2086 step_index_i32: &mut i32,
2087) -> u8 {
2088 let step = ADPCM_STEP_TABLE[*step_index_i32 as usize];
2089 let mut diff = target_sample_i32 - *predictor_i32;
2090 let mut adpcm_nibble = 0_u8;
2091 if diff < 0 {
2092 adpcm_nibble |= 0x08;
2093 diff = -diff;
2094 }
2095
2096 let mut delta = step >> 3;
2097 if diff >= step {
2098 adpcm_nibble |= 0x04;
2099 diff -= step;
2100 delta += step;
2101 }
2102 if diff >= (step >> 1) {
2103 adpcm_nibble |= 0x02;
2104 diff -= step >> 1;
2105 delta += step >> 1;
2106 }
2107 if diff >= (step >> 2) {
2108 adpcm_nibble |= 0x01;
2109 delta += step >> 2;
2110 }
2111
2112 if (adpcm_nibble & 0x08) != 0 {
2113 *predictor_i32 -= delta;
2114 } else {
2115 *predictor_i32 += delta;
2116 }
2117
2118 if *predictor_i32 < i16::MIN as i32 {
2119 *predictor_i32 = i16::MIN as i32;
2120 } else if *predictor_i32 > i16::MAX as i32 {
2121 *predictor_i32 = i16::MAX as i32;
2122 }
2123 *step_index_i32 += ADPCM_INDEX_TABLE[adpcm_nibble as usize];
2124 if *step_index_i32 < 0 {
2125 *step_index_i32 = 0;
2126 } else if *step_index_i32 > 88 {
2127 *step_index_i32 = 88;
2128 }
2129
2130 adpcm_nibble
2131}
2132
2133#[doc(hidden)]
2138#[macro_export]
2139macro_rules! pcm_clip {
2140 ($($tt:tt)*) => { $crate::__audio_clip_parse! { $($tt)* } };
2144}
2145
2146#[doc(hidden)]
2147#[macro_export]
2148macro_rules! __audio_clip_parse {
2149 (
2150 $vis:vis $name:ident {
2151 file: $file:expr,
2152 sample_rate_hz: $source_sample_rate_hz:expr,
2153 target_sample_rate_hz: $target_sample_rate_hz:expr $(,)?
2154 }
2155 ) => {
2156 $crate::__audio_clip_dispatch! {
2157 vis: $vis,
2158 name: $name,
2159 file: $file,
2160 source_sample_rate_hz: $source_sample_rate_hz,
2161 target_sample_rate_hz: $target_sample_rate_hz,
2162 }
2163 };
2164 (
2165 $vis:vis $name:ident {
2166 file: $file:expr,
2167 sample_rate_hz: $source_sample_rate_hz:expr,
2168 target_sample_rate_hz: $target_sample_rate_hz:expr,
2169 $(,)?
2170 }
2171 ) => {
2172 $crate::__audio_clip_dispatch! {
2173 vis: $vis,
2174 name: $name,
2175 file: $file,
2176 source_sample_rate_hz: $source_sample_rate_hz,
2177 target_sample_rate_hz: $target_sample_rate_hz,
2178 }
2179 };
2180 (
2181 $vis:vis $name:ident {
2182 file: $file:expr,
2183 sample_rate_hz: $sample_rate_hz:expr $(,)?
2184 }
2185 ) => {
2186 $crate::__audio_clip_dispatch! {
2187 vis: $vis,
2188 name: $name,
2189 file: $file,
2190 source_sample_rate_hz: $sample_rate_hz,
2191 target_sample_rate_hz: $sample_rate_hz,
2192 }
2193 };
2194 (
2195 $vis:vis $name:ident {
2196 file: $file:expr,
2197 sample_rate_hz: $sample_rate_hz:expr,
2198 $(,)?
2199 }
2200 ) => {
2201 $crate::__audio_clip_dispatch! {
2202 vis: $vis,
2203 name: $name,
2204 file: $file,
2205 source_sample_rate_hz: $sample_rate_hz,
2206 target_sample_rate_hz: $sample_rate_hz,
2207 }
2208 };
2209 (
2210 $vis:vis $name:ident {
2211 file: $file:expr,
2212 sample_rate_hz: $sample_rate_hz:expr,
2213 $(,)?
2214 }
2215 ) => {
2216 $crate::__audio_clip_dispatch! {
2217 vis: $vis,
2218 name: $name,
2219 file: $file,
2220 source_sample_rate_hz: $sample_rate_hz,
2221 target_sample_rate_hz: $sample_rate_hz,
2222 }
2223 };
2224 (
2226 $vis:vis $name:ident {
2227 file: $file:expr,
2228 source_sample_rate_hz: $source_sample_rate_hz:expr,
2229 target_sample_rate_hz: $target_sample_rate_hz:expr $(,)?
2230 }
2231 ) => {
2232 $crate::__audio_clip_dispatch! {
2233 vis: $vis,
2234 name: $name,
2235 file: $file,
2236 source_sample_rate_hz: $source_sample_rate_hz,
2237 target_sample_rate_hz: $target_sample_rate_hz,
2238 }
2239 };
2240 (
2241 $vis:vis $name:ident {
2242 file: $file:expr,
2243 source_sample_rate_hz: $sample_rate_hz:expr $(,)?
2244 }
2245 ) => {
2246 $crate::__audio_clip_dispatch! {
2247 vis: $vis,
2248 name: $name,
2249 file: $file,
2250 source_sample_rate_hz: $sample_rate_hz,
2251 target_sample_rate_hz: $sample_rate_hz,
2252 }
2253 };
2254}
2255
2256#[doc(hidden)]
2257#[macro_export]
2258macro_rules! __audio_clip_dispatch {
2259 (
2260 vis: $vis:vis,
2261 name: $name:ident,
2262 file: $file:expr,
2263 source_sample_rate_hz: $source_sample_rate_hz:expr,
2264 target_sample_rate_hz: $target_sample_rate_hz:expr $(,)?
2265 ) => {
2266 $crate::__audio_clip_impl! {
2267 vis: $vis,
2268 name: $name,
2269 file: $file,
2270 source_sample_rate_hz: $source_sample_rate_hz,
2271 target_sample_rate_hz: $target_sample_rate_hz,
2272 }
2273 };
2274}
2275
2276#[doc(hidden)]
2277#[macro_export]
2278macro_rules! __audio_clip_impl {
2279 (
2280 vis: $vis:vis,
2281 name: $name:ident,
2282 file: $file:expr,
2283 source_sample_rate_hz: $source_sample_rate_hz:expr,
2284 target_sample_rate_hz: $target_sample_rate_hz:expr $(,)?
2285 ) => {
2286 $crate::__paste! {
2287 const [<$name:upper _SOURCE_SAMPLE_RATE_HZ>]: u32 = $source_sample_rate_hz;
2288 const [<$name:upper _TARGET_SAMPLE_RATE_HZ>]: u32 = $target_sample_rate_hz;
2289
2290 #[allow(non_snake_case)]
2291 #[doc = concat!(
2292 "Audio clip module generated by [`pcm_clip!`](macro@crate::audio_player::pcm_clip).\n\n",
2293 "[`SAMPLE_RATE_HZ`](Self::SAMPLE_RATE_HZ), ",
2294 "[`PCM_SAMPLE_COUNT`](Self::PCM_SAMPLE_COUNT), ",
2295 "[`ADPCM_DATA_LEN`](Self::ADPCM_DATA_LEN), ",
2296 "[`pcm_clip`](Self::pcm_clip), ",
2297 "and [`adpcm_clip`](Self::adpcm_clip)."
2298 )]
2299 $vis mod $name {
2300 const SOURCE_SAMPLE_RATE_HZ: u32 = super::[<$name:upper _SOURCE_SAMPLE_RATE_HZ>];
2303 const TARGET_SAMPLE_RATE_HZ: u32 = super::[<$name:upper _TARGET_SAMPLE_RATE_HZ>];
2304 #[doc = "Sample rate in hertz for this generated clip output."]
2305 pub const SAMPLE_RATE_HZ: u32 = TARGET_SAMPLE_RATE_HZ;
2306 const AUDIO_SAMPLE_BYTES_LEN: usize = include_bytes!($file).len();
2307 const SOURCE_SAMPLE_COUNT: usize = AUDIO_SAMPLE_BYTES_LEN / 2;
2308 #[doc = "Number of samples for uncompressed (PCM) version of this clip."]
2309 pub const PCM_SAMPLE_COUNT: usize = $crate::audio_player::__resampled_sample_count(
2310 SOURCE_SAMPLE_COUNT,
2311 SOURCE_SAMPLE_RATE_HZ,
2312 TARGET_SAMPLE_RATE_HZ,
2313 );
2314 #[doc = "Byte length for compressed (ADPCM) encoding this clip."]
2315 pub const ADPCM_DATA_LEN: usize =
2316 $crate::audio_player::__adpcm_data_len_for_pcm_samples(PCM_SAMPLE_COUNT);
2317
2318 #[allow(dead_code)]
2319 type SourcePcmClip = $crate::audio_player::PcmClipBuf<
2320 { SOURCE_SAMPLE_RATE_HZ },
2321 { SOURCE_SAMPLE_COUNT },
2322 >;
2323
2324 #[doc = "`const` function that returns the uncompressed (PCM) version of this clip."]
2325 #[must_use]
2326 pub const fn pcm_clip() -> $crate::audio_player::PcmClipBuf<
2327 { SAMPLE_RATE_HZ },
2328 { PCM_SAMPLE_COUNT },
2329 > {
2330 assert!(
2331 AUDIO_SAMPLE_BYTES_LEN % 2 == 0,
2332 "audio byte length must be even for s16le"
2333 );
2334
2335 let audio_sample_s16le: &[u8; AUDIO_SAMPLE_BYTES_LEN] = include_bytes!($file);
2336 let mut samples = [0_i16; SOURCE_SAMPLE_COUNT];
2337 let mut sample_index = 0_usize;
2338 while sample_index < SOURCE_SAMPLE_COUNT {
2339 let byte_index = sample_index * 2;
2340 samples[sample_index] = i16::from_le_bytes([
2341 audio_sample_s16le[byte_index],
2342 audio_sample_s16le[byte_index + 1],
2343 ]);
2344 sample_index += 1;
2345 }
2346 $crate::audio_player::__resample_pcm_clip::<
2347 SOURCE_SAMPLE_RATE_HZ,
2348 SOURCE_SAMPLE_COUNT,
2349 TARGET_SAMPLE_RATE_HZ,
2350 PCM_SAMPLE_COUNT,
2351 >($crate::audio_player::__pcm_clip_from_samples::<
2352 SOURCE_SAMPLE_RATE_HZ,
2353 SOURCE_SAMPLE_COUNT,
2354 >(samples))
2355 }
2356
2357 #[doc = "`const` function that returns the compressed (ADPCM) encoding for this clip."]
2358 #[must_use]
2359 pub const fn adpcm_clip() -> $crate::audio_player::AdpcmClipBuf<
2360 { SAMPLE_RATE_HZ },
2361 { ADPCM_DATA_LEN },
2362 > {
2363 pcm_clip().with_adpcm::<ADPCM_DATA_LEN>()
2364 }
2365
2366 }
2367 }
2368 };
2369}
2370
2371#[doc = "Macro to \"compile in\" a compressed (ADPCM) WAV clip from an external file (includes syntax details)."]
2372#[doc = include_str!("audio_player/adpcm_clip_docs.md")]
2373#[doc = include_str!("audio_player/audio_prep_steps_1_2.md")]
2374#[doc = include_str!("audio_player/adpcm_clip_step_3.md")]
2375#[doc(inline)]
2376pub use crate::adpcm_clip;
2377
2378#[doc(hidden)]
2379#[macro_export]
2380macro_rules! adpcm_clip {
2381 ($($tt:tt)*) => { $crate::__adpcm_clip_parse! { $($tt)* } };
2382}
2383
2384#[doc(hidden)]
2385#[macro_export]
2386macro_rules! __adpcm_clip_parse {
2387 (
2388 $vis:vis $name:ident {
2389 file: $file:expr,
2390 target_sample_rate_hz: $target_sample_rate_hz:expr $(,)?
2391 }
2392 ) => {
2393 $crate::__paste! {
2394 const [<$name:upper _TARGET_SAMPLE_RATE_HZ>]: u32 = $target_sample_rate_hz;
2395
2396 #[allow(non_snake_case)]
2397 #[allow(missing_docs)]
2398 $vis mod $name {
2399 const PARSED_WAV: $crate::audio_player::ParsedAdpcmWavHeader =
2402 $crate::audio_player::__parse_adpcm_wav_header(include_bytes!($file));
2403 const SOURCE_SAMPLE_RATE_HZ: u32 = PARSED_WAV.sample_rate_hz;
2404 const TARGET_SAMPLE_RATE_HZ: u32 = super::[<$name:upper _TARGET_SAMPLE_RATE_HZ>];
2405 pub const SAMPLE_RATE_HZ: u32 = TARGET_SAMPLE_RATE_HZ;
2406
2407 const SOURCE_SAMPLE_COUNT: usize = PARSED_WAV.sample_count;
2408 #[doc = "Number of samples for uncompressed (PCM) version of this clip."]
2409 pub const PCM_SAMPLE_COUNT: usize = $crate::audio_player::__resampled_sample_count(
2410 SOURCE_SAMPLE_COUNT,
2411 SOURCE_SAMPLE_RATE_HZ,
2412 TARGET_SAMPLE_RATE_HZ,
2413 );
2414 const BLOCK_ALIGN: usize = PARSED_WAV.block_align;
2415 const SOURCE_DATA_LEN: usize = PARSED_WAV.data_chunk_len;
2416 #[doc = "Byte length for compressed (ADPCM) encoding this clip."]
2417 pub const ADPCM_DATA_LEN: usize = if TARGET_SAMPLE_RATE_HZ == SOURCE_SAMPLE_RATE_HZ {
2418 SOURCE_DATA_LEN
2419 } else {
2420 $crate::audio_player::__adpcm_data_len_for_pcm_samples_with_block_align(
2421 PCM_SAMPLE_COUNT,
2422 BLOCK_ALIGN,
2423 )
2424 };
2425 type SourceAdpcmClip = $crate::audio_player::AdpcmClipBuf<SOURCE_SAMPLE_RATE_HZ, SOURCE_DATA_LEN>;
2426
2427 #[must_use]
2428 const fn source_adpcm_clip() -> SourceAdpcmClip {
2429 let wav_bytes = include_bytes!($file);
2430 let parsed_wav = $crate::audio_player::__parse_adpcm_wav_header(wav_bytes);
2431 assert!(parsed_wav.block_align <= u16::MAX as usize, "block_align too large");
2432 assert!(
2433 parsed_wav.samples_per_block <= u16::MAX as usize,
2434 "samples_per_block too large"
2435 );
2436
2437 let mut adpcm_data = [0_u8; SOURCE_DATA_LEN];
2438 let mut data_index = 0usize;
2439 while data_index < SOURCE_DATA_LEN {
2440 adpcm_data[data_index] = wav_bytes[parsed_wav.data_chunk_start + data_index];
2441 data_index += 1;
2442 }
2443
2444 $crate::audio_player::__adpcm_clip_from_parts(
2445 parsed_wav.block_align as u16,
2446 parsed_wav.samples_per_block as u16,
2447 parsed_wav.sample_count,
2448 adpcm_data,
2449 )
2450 }
2451
2452 #[doc = "`const` function that returns the uncompressed (PCM) version of this clip."]
2453 #[must_use]
2454 pub const fn pcm_clip() -> $crate::audio_player::PcmClipBuf<SAMPLE_RATE_HZ, PCM_SAMPLE_COUNT> {
2455 $crate::audio_player::__resample_pcm_clip::<
2456 SOURCE_SAMPLE_RATE_HZ,
2457 SOURCE_SAMPLE_COUNT,
2458 TARGET_SAMPLE_RATE_HZ,
2459 PCM_SAMPLE_COUNT,
2460 >(source_adpcm_clip().with_pcm::<SOURCE_SAMPLE_COUNT>())
2461 }
2462
2463 #[doc = "`const` function that returns the compressed (ADPCM) encoding for this clip."]
2464 #[must_use]
2465 pub const fn adpcm_clip() -> $crate::audio_player::AdpcmClipBuf<SAMPLE_RATE_HZ, ADPCM_DATA_LEN> {
2466 if TARGET_SAMPLE_RATE_HZ == SOURCE_SAMPLE_RATE_HZ {
2467 let wav_bytes = include_bytes!($file);
2468 let parsed_wav = $crate::audio_player::__parse_adpcm_wav_header(wav_bytes);
2469 assert!(parsed_wav.block_align <= u16::MAX as usize, "block_align too large");
2470 assert!(
2471 parsed_wav.samples_per_block <= u16::MAX as usize,
2472 "samples_per_block too large"
2473 );
2474 let mut adpcm_data = [0_u8; ADPCM_DATA_LEN];
2475 let mut data_index = 0usize;
2476 while data_index < ADPCM_DATA_LEN {
2477 adpcm_data[data_index] =
2478 wav_bytes[parsed_wav.data_chunk_start + data_index];
2479 data_index += 1;
2480 }
2481 $crate::audio_player::__adpcm_clip_from_parts(
2482 parsed_wav.block_align as u16,
2483 parsed_wav.samples_per_block as u16,
2484 parsed_wav.sample_count,
2485 adpcm_data,
2486 )
2487 } else {
2488 $crate::audio_player::__pcm_with_adpcm_block_align::<
2489 SAMPLE_RATE_HZ,
2490 PCM_SAMPLE_COUNT,
2491 ADPCM_DATA_LEN,
2492 >(&pcm_clip(), BLOCK_ALIGN)
2493 }
2494 }
2495
2496 }
2497 }
2498 };
2499
2500 (
2501 $vis:vis $name:ident {
2502 file: $file:expr $(,)?
2503 }
2504 ) => {
2505 $crate::__adpcm_clip_parse! {
2506 $vis $name {
2507 file: $file,
2508 target_sample_rate_hz: $crate::audio_player::__parse_adpcm_wav_header(include_bytes!($file)).sample_rate_hz,
2509 }
2510 }
2511 };
2512}
2513
2514#[doc(hidden)]
2526#[macro_export]
2527macro_rules! tone {
2528 ($frequency_hz:expr, $sample_rate_hz:expr, $duration:expr) => {
2529 $crate::audio_player::__tone_pcm_clip_with_duration::<
2530 { $sample_rate_hz },
2531 { $crate::audio_player::__samples_for_duration($duration, $sample_rate_hz) },
2532 >($frequency_hz, $duration)
2533 };
2534}
2535
2536#[doc = "Macro to \"compile in\" an uncompressed (PCM) clip from an external file (includes syntax details)."]
2537#[doc = include_str!("audio_player/pcm_clip_docs.md")]
2538#[doc = include_str!("audio_player/audio_prep_steps_1_2.md")]
2539#[doc = include_str!("audio_player/pcm_clip_step_3.md")]
2540#[doc(inline)]
2541pub use crate::pcm_clip;
2542#[doc(inline)]
2543pub use crate::tone;