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
463 let block_align = adpcm_clip.block_align() as usize;
464 for adpcm_block in adpcm_clip.data().chunks_exact(block_align) {
465 if adpcm_block.len() < 4 {
466 return ControlFlow::Continue(());
467 }
468
469 let runtime_volume = audio_player_static.effective_runtime_volume();
470 let mut predictor_i32 = match read_i16_le(adpcm_block, 0) {
471 Some(value) => value as i32,
472 None => return ControlFlow::Continue(()),
473 };
474 let mut step_index_i32 = adpcm_block[2] as i32;
475 if !(0..=88).contains(&step_index_i32) {
476 return ControlFlow::Continue(());
477 }
478
479 sample_buffer[sample_buffer_len] = stereo_sample(scale_sample_with_volume(
480 predictor_i32 as i16,
481 runtime_volume,
482 ));
483 sample_buffer_len += 1;
484 if sample_buffer_len == SAMPLE_BUFFER_LEN {
485 if output
486 .write_stereo_words(sample_buffer, sample_buffer_len)
487 .await
488 .is_err()
489 {
490 return ControlFlow::Continue(());
491 }
492 output.after_write().await;
493 sample_buffer_len = 0;
494 if let Some(next_audio_command) = audio_player_static.try_take_command() {
495 return ControlFlow::Break(next_audio_command);
496 }
497 }
498
499 let mut samples_decoded_in_block = 1usize;
500 let samples_per_block = adpcm_clip.samples_per_block() as usize;
501
502 for adpcm_byte in &adpcm_block[4..] {
503 for adpcm_nibble in [adpcm_byte & 0x0F, adpcm_byte >> 4] {
504 if samples_decoded_in_block >= samples_per_block {
505 break;
506 }
507
508 let decoded_sample_i16 = decode_adpcm_nibble_const(
509 adpcm_nibble,
510 &mut predictor_i32,
511 &mut step_index_i32,
512 );
513 sample_buffer[sample_buffer_len] =
514 stereo_sample(scale_sample_with_volume(decoded_sample_i16, runtime_volume));
515 sample_buffer_len += 1;
516 samples_decoded_in_block += 1;
517
518 if sample_buffer_len == SAMPLE_BUFFER_LEN {
519 if output
520 .write_stereo_words(sample_buffer, sample_buffer_len)
521 .await
522 .is_err()
523 {
524 return ControlFlow::Continue(());
525 }
526 output.after_write().await;
527 sample_buffer_len = 0;
528 if let Some(next_audio_command) = audio_player_static.try_take_command() {
529 return ControlFlow::Break(next_audio_command);
530 }
531 }
532 }
533 }
534
535 if let Some(next_audio_command) = audio_player_static.try_take_command() {
536 return ControlFlow::Break(next_audio_command);
537 }
538 }
539
540 if sample_buffer_len != 0 {
541 sample_buffer[sample_buffer_len..].fill(stereo_sample(0));
542 if output
543 .write_stereo_words(sample_buffer, sample_buffer_len)
544 .await
545 .is_err()
546 {
547 return ControlFlow::Continue(());
548 }
549 output.after_write().await;
550 if let Some(next_audio_command) = audio_player_static.try_take_command() {
551 return ControlFlow::Break(next_audio_command);
552 }
553 }
554
555 ControlFlow::Continue(())
556}
557
558async fn play_silence_duration_once<
559 Output: AudioOutputSink<SAMPLE_BUFFER_LEN>,
560 const SAMPLE_BUFFER_LEN: usize,
561 const MAX_CLIPS: usize,
562 const SAMPLE_RATE_HZ: u32,
563>(
564 output: &mut Output,
565 duration: Duration,
566 sample_buffer: &mut [u32; SAMPLE_BUFFER_LEN],
567 audio_player_static: &'static AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>,
568) -> ControlFlow<AudioCommand<MAX_CLIPS, SAMPLE_RATE_HZ>, ()> {
569 let silence_sample_count = __samples_for_duration(duration, SAMPLE_RATE_HZ);
570 let mut remaining_sample_count = silence_sample_count;
571 sample_buffer.fill(stereo_sample(0));
572
573 while remaining_sample_count > 0 {
574 let chunk_sample_count = remaining_sample_count.min(SAMPLE_BUFFER_LEN);
575 if output
576 .write_stereo_words(sample_buffer, chunk_sample_count)
577 .await
578 .is_err()
579 {
580 return ControlFlow::Continue(());
581 }
582 output.after_write().await;
583 remaining_sample_count -= chunk_sample_count;
584 if let Some(next_audio_command) = audio_player_static.try_take_command() {
585 return ControlFlow::Break(next_audio_command);
586 }
587 }
588
589 ControlFlow::Continue(())
590}
591
592#[inline]
593fn read_i16_le(bytes: &[u8], byte_offset: usize) -> Option<i16> {
594 let end_offset = byte_offset.checked_add(2)?;
595 if end_offset > bytes.len() {
596 return None;
597 }
598 Some(i16::from_le_bytes([
599 bytes[byte_offset],
600 bytes[byte_offset + 1],
601 ]))
602}
603
604pub enum AtEnd {
611 Loop,
613 Stop,
615}
616
617pub struct AdpcmClip<const SAMPLE_RATE_HZ: u32, T: ?Sized = [u8]> {
621 block_align: u16,
622 samples_per_block: u16,
623 data: T,
624}
625
626pub type AdpcmClipBuf<const SAMPLE_RATE_HZ: u32, const DATA_LEN: usize> =
628 AdpcmClip<SAMPLE_RATE_HZ, [u8; DATA_LEN]>;
629
630impl<const SAMPLE_RATE_HZ: u32, T: ?Sized> AdpcmClip<SAMPLE_RATE_HZ, T> {
631 #[must_use]
633 pub fn block_align(&self) -> u16 {
634 self.block_align
635 }
636
637 #[must_use]
639 pub fn samples_per_block(&self) -> u16 {
640 self.samples_per_block
641 }
642
643 #[must_use]
645 pub fn data(&self) -> &T {
646 &self.data
647 }
648}
649
650impl<const SAMPLE_RATE_HZ: u32, const DATA_LEN: usize> AdpcmClip<SAMPLE_RATE_HZ, [u8; DATA_LEN]> {
656 #[must_use]
658 pub(crate) const fn new(
659 block_align: u16,
660 samples_per_block: u16,
661 data: [u8; DATA_LEN],
662 ) -> Self {
663 assert!(SAMPLE_RATE_HZ > 0, "sample_rate_hz must be > 0");
664 assert!(block_align >= 5, "block_align must be >= 5");
665 assert!(samples_per_block > 0, "samples_per_block must be > 0");
666 assert!(
667 DATA_LEN % block_align as usize == 0,
668 "adpcm data length must be block aligned"
669 );
670 Self {
671 block_align,
672 samples_per_block,
673 data,
674 }
675 }
676
677 #[must_use]
683 pub const fn with_pcm<const SAMPLE_COUNT: usize>(
684 &self,
685 ) -> PcmClipBuf<SAMPLE_RATE_HZ, SAMPLE_COUNT> {
686 let block_align = self.block_align as usize;
687 assert!(block_align >= 5, "block_align must be >= 5");
688 assert!(
689 DATA_LEN % block_align == 0,
690 "adpcm data length must be block aligned"
691 );
692
693 let samples_per_block = self.samples_per_block as usize;
694 assert!(samples_per_block > 0, "samples_per_block must be > 0");
695 let expected_sample_count = (DATA_LEN / block_align) * samples_per_block;
696 assert!(
697 SAMPLE_COUNT == expected_sample_count,
698 "sample count must match decoded ADPCM length"
699 );
700
701 let mut samples = [0_i16; SAMPLE_COUNT];
702 if SAMPLE_COUNT == 0 {
703 assert!(SAMPLE_RATE_HZ > 0, "sample_rate_hz must be > 0");
704 return PcmClip { samples };
705 }
706
707 let mut sample_index = 0usize;
708 let mut block_start = 0usize;
709 while block_start < DATA_LEN {
711 let mut predictor_i32 = read_i16_le_const(&self.data, block_start) as i32;
712 let mut step_index_i32 = self.data[block_start + 2] as i32;
713 assert!(step_index_i32 >= 0, "ADPCM step_index must be >= 0");
714 assert!(step_index_i32 <= 88, "ADPCM step_index must be <= 88");
715
716 samples[sample_index] = predictor_i32 as i16;
717 sample_index += 1;
718 let mut decoded_in_block = 1usize;
719
720 let mut adpcm_byte_offset = block_start + 4;
721 let adpcm_block_end = block_start + block_align;
722 while adpcm_byte_offset < adpcm_block_end {
724 let adpcm_byte = self.data[adpcm_byte_offset];
725 let adpcm_nibble_low = adpcm_byte & 0x0F;
726 let adpcm_nibble_high = adpcm_byte >> 4;
727
728 if decoded_in_block < samples_per_block {
729 samples[sample_index] = decode_adpcm_nibble_const(
730 adpcm_nibble_low,
731 &mut predictor_i32,
732 &mut step_index_i32,
733 );
734 sample_index += 1;
735 decoded_in_block += 1;
736 }
737 if decoded_in_block < samples_per_block {
738 samples[sample_index] = decode_adpcm_nibble_const(
739 adpcm_nibble_high,
740 &mut predictor_i32,
741 &mut step_index_i32,
742 );
743 sample_index += 1;
744 decoded_in_block += 1;
745 }
746
747 adpcm_byte_offset += 1;
748 }
749
750 block_start += block_align;
751 }
752
753 assert!(SAMPLE_RATE_HZ > 0, "sample_rate_hz must be > 0");
754 PcmClip { samples }
755 }
756
757 #[must_use]
763 pub const fn with_gain(self, gain: Gain) -> Self {
764 let block_align = self.block_align as usize;
765 assert!(block_align >= 5, "block_align must be >= 5");
766 assert!(
767 DATA_LEN % block_align == 0,
768 "adpcm data length must be block aligned"
769 );
770
771 let samples_per_block = self.samples_per_block as usize;
772 assert!(samples_per_block > 0, "samples_per_block must be > 0");
773 let max_samples_per_block = __adpcm_samples_per_block(block_align);
774 assert!(
775 samples_per_block <= max_samples_per_block,
776 "samples_per_block exceeds block_align capacity"
777 );
778
779 let mut gained_data = [0_u8; DATA_LEN];
780 let mut block_start = 0usize;
781 while block_start < DATA_LEN {
783 let mut source_predictor_i32 = read_i16_le_const(&self.data, block_start) as i32;
784 let mut source_step_index_i32 = self.data[block_start + 2] as i32;
785 assert!(
786 source_step_index_i32 >= 0 && source_step_index_i32 <= 88,
787 "ADPCM step_index must be in 0..=88"
788 );
789
790 let scaled_first_sample_i16 =
791 scale_sample_with_linear(source_predictor_i32 as i16, gain.linear());
792 let mut destination_predictor_i32 = scaled_first_sample_i16 as i32;
793 let mut destination_step_index_i32 = source_step_index_i32;
794
795 let scaled_first_sample_bytes = scaled_first_sample_i16.to_le_bytes();
796 gained_data[block_start] = scaled_first_sample_bytes[0];
797 gained_data[block_start + 1] = scaled_first_sample_bytes[1];
798 gained_data[block_start + 2] = destination_step_index_i32 as u8;
799 gained_data[block_start + 3] = 0;
800
801 let mut decoded_in_block = 1usize;
802 let mut source_byte_offset = block_start + 4;
803 let mut destination_byte_offset = block_start + 4;
804 let block_end = block_start + block_align;
805
806 while source_byte_offset < block_end {
808 let source_byte = self.data[source_byte_offset];
809 let mut destination_byte = 0_u8;
810
811 let mut nibble_index = 0usize;
812 while nibble_index < 2 {
814 if decoded_in_block < samples_per_block {
815 let source_nibble = if nibble_index == 0 {
816 source_byte & 0x0F
817 } else {
818 source_byte >> 4
819 };
820 let decoded_sample_i16 = decode_adpcm_nibble_const(
821 source_nibble,
822 &mut source_predictor_i32,
823 &mut source_step_index_i32,
824 );
825 let scaled_sample_i32 =
826 scale_sample_with_linear(decoded_sample_i16, gain.linear()) as i32;
827 let destination_nibble = encode_adpcm_nibble(
828 scaled_sample_i32,
829 &mut destination_predictor_i32,
830 &mut destination_step_index_i32,
831 );
832 destination_byte |= destination_nibble << (nibble_index * 4);
833 decoded_in_block += 1;
834 }
835 nibble_index += 1;
836 }
837
838 gained_data[destination_byte_offset] = destination_byte;
839 source_byte_offset += 1;
840 destination_byte_offset += 1;
841 }
842
843 block_start += block_align;
844 }
845
846 Self::new(self.block_align, self.samples_per_block, gained_data)
847 }
848}
849
850#[derive(Clone, Copy)]
852#[doc(hidden)]
853pub struct ParsedAdpcmWavHeader {
854 pub sample_rate_hz: u32,
856 pub block_align: usize,
858 pub samples_per_block: usize,
860 pub data_chunk_start: usize,
862 pub data_chunk_len: usize,
864 pub sample_count: usize,
866}
867
868#[must_use]
870#[doc(hidden)]
871pub const fn __parse_adpcm_wav_header(wav_bytes: &[u8]) -> ParsedAdpcmWavHeader {
872 if wav_bytes.len() < 12 {
873 panic!("WAV file too small");
874 }
875 if !wav_tag_eq(wav_bytes, 0, *b"RIFF") {
876 panic!("Missing RIFF header");
877 }
878 if !wav_tag_eq(wav_bytes, 8, *b"WAVE") {
879 panic!("Missing WAVE header");
880 }
881
882 let mut chunk_offset = 12usize;
883 let mut sample_rate_hz = 0u32;
884 let mut block_align = 0usize;
885 let mut samples_per_block = 0usize;
886 let mut fmt_found = false;
887 let mut data_chunk_start = 0usize;
888 let mut data_chunk_end = 0usize;
889 let mut data_found = false;
890
891 while chunk_offset + 8 <= wav_bytes.len() {
893 let chunk_size = read_u32_le_const(wav_bytes, chunk_offset + 4) as usize;
894 let chunk_data_start = chunk_offset + 8;
895 if chunk_data_start > wav_bytes.len() || chunk_size > wav_bytes.len() - chunk_data_start {
896 panic!("WAV chunk overruns file");
897 }
898 let chunk_data_end = chunk_data_start + chunk_size;
899
900 if wav_tag_eq(wav_bytes, chunk_offset, *b"fmt ") {
901 if chunk_size < 16 {
902 panic!("fmt chunk too small");
903 }
904
905 let audio_format = read_u16_le_const(wav_bytes, chunk_data_start);
906 let channels = read_u16_le_const(wav_bytes, chunk_data_start + 2);
907 sample_rate_hz = read_u32_le_const(wav_bytes, chunk_data_start + 4);
908 block_align = read_u16_le_const(wav_bytes, chunk_data_start + 12) as usize;
909 let bits_per_sample = read_u16_le_const(wav_bytes, chunk_data_start + 14);
910
911 if audio_format != 0x0011 {
912 panic!("Expected ADPCM WAV format");
913 }
914 if channels != 1 {
915 panic!("Expected mono ADPCM WAV");
916 }
917 if bits_per_sample != 4 {
918 panic!("Expected 4-bit ADPCM");
919 }
920 if block_align < 5 {
921 panic!("ADPCM block_align too small");
922 }
923
924 let derived_samples_per_block = derive_samples_per_block_const(block_align);
925 samples_per_block = if chunk_size >= 22 {
926 read_u16_le_const(wav_bytes, chunk_data_start + 18) as usize
927 } else {
928 derived_samples_per_block
929 };
930 if samples_per_block != derived_samples_per_block {
931 panic!("Unexpected ADPCM samples_per_block");
932 }
933 fmt_found = true;
934 } else if wav_tag_eq(wav_bytes, chunk_offset, *b"data") {
935 data_chunk_start = chunk_data_start;
936 data_chunk_end = chunk_data_end;
937 data_found = true;
938 }
939
940 let padded_chunk_size = chunk_size + (chunk_size & 1);
941 if chunk_data_start > usize::MAX - padded_chunk_size {
942 panic!("WAV chunk traversal overflow");
943 }
944 chunk_offset = chunk_data_start + padded_chunk_size;
945 }
946
947 if !fmt_found {
948 panic!("Missing fmt chunk");
949 }
950 if !data_found {
951 panic!("Missing data chunk");
952 }
953 let data_chunk_len = data_chunk_end - data_chunk_start;
954 if data_chunk_len % block_align != 0 {
955 panic!("data chunk is not block aligned");
956 }
957
958 ParsedAdpcmWavHeader {
959 sample_rate_hz,
960 block_align,
961 samples_per_block,
962 data_chunk_start,
963 data_chunk_len,
964 sample_count: (data_chunk_len / block_align) * samples_per_block,
965 }
966}
967
968const fn wav_tag_eq(wav_bytes: &[u8], byte_offset: usize, tag_bytes: [u8; 4]) -> bool {
969 if byte_offset > wav_bytes.len().saturating_sub(4) {
970 return false;
971 }
972 wav_bytes[byte_offset] == tag_bytes[0]
973 && wav_bytes[byte_offset + 1] == tag_bytes[1]
974 && wav_bytes[byte_offset + 2] == tag_bytes[2]
975 && wav_bytes[byte_offset + 3] == tag_bytes[3]
976}
977
978const fn derive_samples_per_block_const(block_align: usize) -> usize {
979 if block_align < 4 {
980 panic!("ADPCM block_align underflow");
981 }
982 ((block_align - 4) * 2) + 1
983}
984
985const ADPCM_INDEX_TABLE: [i32; 16] = [-1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8];
986const ADPCM_STEP_TABLE: [i32; 89] = [
987 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66,
988 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449,
989 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272,
990 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493,
991 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767,
992];
993
994#[doc(hidden)]
998#[must_use]
999pub const fn __adpcm_samples_per_block(block_align: usize) -> usize {
1000 if block_align < 5 {
1001 panic!("block_align must be >= 5 for ADPCM");
1002 }
1003 derive_samples_per_block_const(block_align)
1004}
1005
1006#[doc(hidden)]
1008#[must_use]
1009pub const fn __adpcm_data_len_for_pcm_samples(sample_count: usize) -> usize {
1010 __adpcm_data_len_for_pcm_samples_with_block_align(sample_count, ADPCM_ENCODE_BLOCK_ALIGN)
1011}
1012
1013#[doc(hidden)]
1016#[must_use]
1017pub const fn __adpcm_data_len_for_pcm_samples_with_block_align(
1018 sample_count: usize,
1019 block_align: usize,
1020) -> usize {
1021 let samples_per_block = __adpcm_samples_per_block(block_align);
1022 let block_count = if sample_count == 0 {
1023 0
1024 } else {
1025 ((sample_count - 1) / samples_per_block) + 1
1026 };
1027 block_count * block_align
1028}
1029
1030const fn read_u16_le_const(bytes: &[u8], byte_offset: usize) -> u16 {
1031 if byte_offset > bytes.len().saturating_sub(2) {
1032 panic!("read_u16_le_const out of bounds");
1033 }
1034 u16::from_le_bytes([bytes[byte_offset], bytes[byte_offset + 1]])
1035}
1036
1037const fn read_i16_le_const(bytes: &[u8], byte_offset: usize) -> i16 {
1038 if byte_offset > bytes.len().saturating_sub(2) {
1039 panic!("read_i16_le_const out of bounds");
1040 }
1041 i16::from_le_bytes([bytes[byte_offset], bytes[byte_offset + 1]])
1042}
1043
1044const fn read_u32_le_const(bytes: &[u8], byte_offset: usize) -> u32 {
1045 if byte_offset > bytes.len().saturating_sub(4) {
1046 panic!("read_u32_le_const out of bounds");
1047 }
1048 u32::from_le_bytes([
1049 bytes[byte_offset],
1050 bytes[byte_offset + 1],
1051 bytes[byte_offset + 2],
1052 bytes[byte_offset + 3],
1053 ])
1054}
1055
1056#[doc(hidden)]
1059pub enum PlaybackClip<const SAMPLE_RATE_HZ: u32> {
1060 Pcm(&'static PcmClip<SAMPLE_RATE_HZ>),
1061 Adpcm(&'static AdpcmClip<SAMPLE_RATE_HZ>),
1062 Silence(Duration),
1063}
1064
1065#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1073pub struct SilenceClip {
1074 duration: Duration,
1075}
1076
1077impl SilenceClip {
1078 #[must_use]
1081 pub const fn new(duration: core::time::Duration) -> Self {
1082 Self { duration }
1083 }
1084
1085 #[must_use]
1088 pub const fn duration(self) -> core::time::Duration {
1089 self.duration
1090 }
1091}
1092
1093#[allow(private_bounds)]
1100pub trait Playable<const SAMPLE_RATE_HZ: u32>: sealed::PlayableSealed<SAMPLE_RATE_HZ> {}
1101
1102impl<const SAMPLE_RATE_HZ: u32, T: ?Sized> Playable<SAMPLE_RATE_HZ> for T where
1103 T: sealed::PlayableSealed<SAMPLE_RATE_HZ>
1104{
1105}
1106
1107#[allow(async_fn_in_trait)]
1335pub trait AudioPlayer<const SAMPLE_RATE_HZ: u32> {
1336 const SAMPLE_RATE_HZ: u32;
1338 const MAX_CLIPS: usize;
1340 const INITIAL_VOLUME: Volume;
1342 const MAX_VOLUME: Volume;
1344
1345 fn play<I>(&self, audio_clips: I, at_end: AtEnd)
1352 where
1353 I: IntoIterator<Item = &'static dyn Playable<SAMPLE_RATE_HZ>>;
1354
1355 fn stop(&self);
1359
1360 async fn wait_until_stopped(&self);
1364
1365 fn set_volume(&self, volume: Volume);
1369
1370 fn volume(&self) -> Volume;
1374}
1375
1376mod sealed {
1377 use super::{AdpcmClip, PcmClip, PlaybackClip, SilenceClip};
1378
1379 pub(crate) trait PlayableSealed<const SAMPLE_RATE_HZ: u32> {
1380 fn playback_clip(&'static self) -> PlaybackClip<SAMPLE_RATE_HZ>;
1381 }
1382
1383 impl<const SAMPLE_RATE_HZ: u32> PlayableSealed<SAMPLE_RATE_HZ> for PcmClip<SAMPLE_RATE_HZ> {
1384 fn playback_clip(&'static self) -> PlaybackClip<SAMPLE_RATE_HZ> {
1385 PlaybackClip::Pcm(self)
1386 }
1387 }
1388
1389 impl<const SAMPLE_RATE_HZ: u32, const SAMPLE_COUNT: usize> PlayableSealed<SAMPLE_RATE_HZ>
1390 for PcmClip<SAMPLE_RATE_HZ, [i16; SAMPLE_COUNT]>
1391 {
1392 fn playback_clip(&'static self) -> PlaybackClip<SAMPLE_RATE_HZ> {
1393 PlaybackClip::Pcm(self)
1394 }
1395 }
1396
1397 impl<const SAMPLE_RATE_HZ: u32> PlayableSealed<SAMPLE_RATE_HZ> for AdpcmClip<SAMPLE_RATE_HZ> {
1398 fn playback_clip(&'static self) -> PlaybackClip<SAMPLE_RATE_HZ> {
1399 PlaybackClip::Adpcm(self)
1400 }
1401 }
1402
1403 impl<const SAMPLE_RATE_HZ: u32, const DATA_LEN: usize> PlayableSealed<SAMPLE_RATE_HZ>
1404 for AdpcmClip<SAMPLE_RATE_HZ, [u8; DATA_LEN]>
1405 {
1406 fn playback_clip(&'static self) -> PlaybackClip<SAMPLE_RATE_HZ> {
1407 PlaybackClip::Adpcm(self)
1408 }
1409 }
1410
1411 impl<const SAMPLE_RATE_HZ: u32> PlayableSealed<SAMPLE_RATE_HZ> for SilenceClip {
1412 fn playback_clip(&'static self) -> PlaybackClip<SAMPLE_RATE_HZ> {
1413 PlaybackClip::Silence(self.duration())
1414 }
1415 }
1416}
1417
1418pub struct PcmClip<const SAMPLE_RATE_HZ: u32, T: ?Sized = [i16]> {
1425 samples: T,
1426}
1427
1428impl<const SAMPLE_RATE_HZ: u32, T: ?Sized> PcmClip<SAMPLE_RATE_HZ, T> {
1429 #[must_use]
1431 pub fn samples(&self) -> &T {
1432 &self.samples
1433 }
1434}
1435
1436pub type PcmClipBuf<const SAMPLE_RATE_HZ: u32, const SAMPLE_COUNT: usize> =
1446 PcmClip<SAMPLE_RATE_HZ, [i16; SAMPLE_COUNT]>;
1447
1448impl<const SAMPLE_RATE_HZ: u32, const SAMPLE_COUNT: usize>
1454 PcmClip<SAMPLE_RATE_HZ, [i16; SAMPLE_COUNT]>
1455{
1456 #[must_use]
1467 pub const fn with_gain(self, gain: Gain) -> Self {
1468 assert!(SAMPLE_RATE_HZ > 0, "sample_rate_hz must be > 0");
1469 let mut scaled_samples = [0_i16; SAMPLE_COUNT];
1470 let mut sample_index = 0_usize;
1471 while sample_index < SAMPLE_COUNT {
1473 scaled_samples[sample_index] =
1474 scale_sample_with_linear(self.samples[sample_index], gain.linear());
1475 sample_index += 1;
1476 }
1477 Self {
1478 samples: scaled_samples,
1479 }
1480 }
1481
1482 #[must_use]
1490 pub(crate) const fn with_attack_release(self, attack: Duration, release: Duration) -> Self {
1491 assert!(SAMPLE_RATE_HZ > 0, "sample_rate_hz must be > 0");
1492 let attack_sample_count = __samples_for_duration(attack, SAMPLE_RATE_HZ);
1493 let release_sample_count = __samples_for_duration(release, SAMPLE_RATE_HZ);
1494 self.with_attack_release_sample_count(attack_sample_count, release_sample_count)
1495 }
1496
1497 #[must_use]
1498 const fn with_attack_release_sample_count(
1499 self,
1500 attack_sample_count: usize,
1501 release_sample_count: usize,
1502 ) -> Self {
1503 assert!(
1504 attack_sample_count <= SAMPLE_COUNT,
1505 "attack duration must fit within clip duration"
1506 );
1507 assert!(
1508 release_sample_count <= SAMPLE_COUNT,
1509 "release duration must fit within clip duration"
1510 );
1511 assert!(
1512 attack_sample_count + release_sample_count <= SAMPLE_COUNT,
1513 "attack + release must fit within clip duration"
1514 );
1515
1516 let mut shaped_samples = self.samples;
1517
1518 if attack_sample_count > 0 {
1519 let attack_sample_count_i32 = attack_sample_count as i32;
1520 let mut sample_index = 0usize;
1521 while sample_index < attack_sample_count {
1523 let envelope_numerator_i32 = sample_index as i32;
1524 shaped_samples[sample_index] = scale_sample_with_linear(
1525 shaped_samples[sample_index],
1526 (envelope_numerator_i32 * i16::MAX as i32) / attack_sample_count_i32,
1527 );
1528 sample_index += 1;
1529 }
1530 }
1531
1532 if release_sample_count > 0 {
1533 let release_sample_count_i32 = release_sample_count as i32;
1534 let release_start_index = SAMPLE_COUNT - release_sample_count;
1535 let mut release_index = 0usize;
1536 while release_index < release_sample_count {
1538 let sample_index = release_start_index + release_index;
1539 let envelope_numerator_i32 = (release_sample_count - release_index) as i32;
1540 shaped_samples[sample_index] = scale_sample_with_linear(
1541 shaped_samples[sample_index],
1542 (envelope_numerator_i32 * i16::MAX as i32) / release_sample_count_i32,
1543 );
1544 release_index += 1;
1545 }
1546 }
1547
1548 Self {
1549 samples: shaped_samples,
1550 }
1551 }
1552
1553 #[must_use]
1558 pub const fn with_adpcm<const DATA_LEN: usize>(
1559 &self,
1560 ) -> AdpcmClipBuf<SAMPLE_RATE_HZ, DATA_LEN> {
1561 self.with_adpcm_block_align::<DATA_LEN>(ADPCM_ENCODE_BLOCK_ALIGN)
1562 }
1563
1564 #[must_use]
1565 pub(crate) const fn with_adpcm_block_align<const DATA_LEN: usize>(
1566 &self,
1567 block_align: usize,
1568 ) -> AdpcmClipBuf<SAMPLE_RATE_HZ, DATA_LEN> {
1569 assert!(block_align >= 5, "block_align must be >= 5");
1570 assert!(
1571 block_align <= u16::MAX as usize,
1572 "block_align must fit in u16"
1573 );
1574 let samples_per_block = __adpcm_samples_per_block(block_align);
1575 assert!(
1576 samples_per_block <= u16::MAX as usize,
1577 "samples_per_block must fit in u16"
1578 );
1579 assert!(
1580 DATA_LEN
1581 == __adpcm_data_len_for_pcm_samples_with_block_align(SAMPLE_COUNT, block_align),
1582 "adpcm data length must match sample count and block_align"
1583 );
1584 if SAMPLE_COUNT == 0 {
1585 return AdpcmClip::new(block_align as u16, samples_per_block as u16, [0; DATA_LEN]);
1586 }
1587
1588 let mut adpcm_data = [0_u8; DATA_LEN];
1589 let mut sample_index = 0usize;
1590 let mut data_index = 0usize;
1591 let payload_len_per_block = block_align - 4;
1592
1593 while sample_index < SAMPLE_COUNT {
1595 let mut predictor_i32 = self.samples[sample_index] as i32;
1596 let mut step_index_i32 = 0_i32;
1597
1598 let predictor_i16 = predictor_i32 as i16;
1599 let predictor_bytes = predictor_i16.to_le_bytes();
1600 adpcm_data[data_index] = predictor_bytes[0];
1601 adpcm_data[data_index + 1] = predictor_bytes[1];
1602 adpcm_data[data_index + 2] = step_index_i32 as u8;
1603 adpcm_data[data_index + 3] = 0;
1604 data_index += 4;
1605 sample_index += 1;
1606
1607 let mut payload_byte_index = 0usize;
1608 while payload_byte_index < payload_len_per_block {
1610 let mut adpcm_byte = 0_u8;
1611
1612 let mut nibble_index = 0usize;
1613 while nibble_index < 2 {
1615 let target_sample_i32 = if sample_index < SAMPLE_COUNT {
1616 self.samples[sample_index] as i32
1617 } else {
1618 predictor_i32
1619 };
1620 let adpcm_nibble = encode_adpcm_nibble(
1621 target_sample_i32,
1622 &mut predictor_i32,
1623 &mut step_index_i32,
1624 );
1625 adpcm_byte |= adpcm_nibble << (nibble_index * 4);
1626 sample_index += 1;
1627 nibble_index += 1;
1628 }
1629
1630 adpcm_data[data_index] = adpcm_byte;
1631 data_index += 1;
1632 payload_byte_index += 1;
1633 }
1634 }
1635
1636 AdpcmClip::new(block_align as u16, samples_per_block as u16, adpcm_data)
1637 }
1638}
1639
1640#[doc(hidden)]
1643pub enum AudioCommand<const MAX_CLIPS: usize, const SAMPLE_RATE_HZ: u32> {
1644 Play {
1645 audio_clips: Vec<PlaybackClip<SAMPLE_RATE_HZ>, MAX_CLIPS>,
1646 at_end: AtEnd,
1647 },
1648 Stop,
1649}
1650
1651#[doc(hidden)]
1654pub struct AudioPlayerStatic<const MAX_CLIPS: usize, const SAMPLE_RATE_HZ: u32> {
1655 command_signal: Signal<CriticalSectionRawMutex, AudioCommand<MAX_CLIPS, SAMPLE_RATE_HZ>>,
1656 stopped_signal: Signal<CriticalSectionRawMutex, ()>,
1657 is_playing: AtomicBool,
1658 has_pending_play: AtomicBool,
1659 max_volume_linear: i32,
1660 runtime_volume_relative_linear: AtomicI32,
1661}
1662
1663impl<const MAX_CLIPS: usize, const SAMPLE_RATE_HZ: u32>
1664 AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>
1665{
1666 #[must_use]
1668 pub const fn new_static() -> Self {
1669 Self::new_static_with_max_volume_and_initial_volume(Volume::MAX, Volume::MAX)
1670 }
1671
1672 #[must_use]
1674 pub const fn new_static_with_max_volume(max_volume: Volume) -> Self {
1675 Self::new_static_with_max_volume_and_initial_volume(max_volume, Volume::MAX)
1676 }
1677
1678 #[must_use]
1681 pub const fn new_static_with_max_volume_and_initial_volume(
1682 max_volume: Volume,
1683 initial_volume: Volume,
1684 ) -> Self {
1685 Self {
1686 command_signal: Signal::new(),
1687 stopped_signal: Signal::new(),
1688 is_playing: AtomicBool::new(false),
1689 has_pending_play: AtomicBool::new(false),
1690 max_volume_linear: max_volume.to_i16() as i32,
1691 runtime_volume_relative_linear: AtomicI32::new(initial_volume.to_i16() as i32),
1692 }
1693 }
1694
1695 fn signal(&self, audio_command: AudioCommand<MAX_CLIPS, SAMPLE_RATE_HZ>) {
1696 self.command_signal.signal(audio_command);
1697 }
1698
1699 fn mark_pending_play(&self) {
1700 self.has_pending_play.store(true, AtomicOrdering::Relaxed);
1701 }
1702
1703 #[doc(hidden)]
1705 pub fn mark_playing(&self) {
1706 self.has_pending_play.store(false, AtomicOrdering::Relaxed);
1707 self.is_playing.store(true, AtomicOrdering::Relaxed);
1708 }
1709
1710 #[doc(hidden)]
1712 pub fn mark_stopped(&self) {
1713 self.has_pending_play.store(false, AtomicOrdering::Relaxed);
1714 self.is_playing.store(false, AtomicOrdering::Relaxed);
1715 self.stopped_signal.signal(());
1716 }
1717
1718 fn is_idle(&self) -> bool {
1719 !self.has_pending_play.load(AtomicOrdering::Relaxed)
1720 && !self.is_playing.load(AtomicOrdering::Relaxed)
1721 }
1722
1723 async fn wait_until_stopped(&self) {
1724 while !self.is_idle() {
1725 self.stopped_signal.wait().await;
1726 }
1727 }
1728
1729 fn set_runtime_volume(&self, volume: Volume) {
1730 self.runtime_volume_relative_linear
1731 .store(volume.to_i16() as i32, Ordering::Relaxed);
1732 }
1733
1734 fn runtime_volume(&self) -> Volume {
1735 Volume::from_i16(self.runtime_volume_relative_linear.load(Ordering::Relaxed) as i16)
1736 }
1737
1738 #[doc(hidden)]
1741 pub fn effective_runtime_volume(&self) -> Volume {
1742 let runtime_volume_relative = self.runtime_volume();
1743 Volume::from_i16(scale_linear(self.max_volume_linear, runtime_volume_relative) as i16)
1744 }
1745
1746 #[doc(hidden)]
1748 pub async fn wait(&self) -> AudioCommand<MAX_CLIPS, SAMPLE_RATE_HZ> {
1749 self.command_signal.wait().await
1750 }
1751
1752 #[doc(hidden)]
1755 pub fn try_take_command(&self) -> Option<AudioCommand<MAX_CLIPS, SAMPLE_RATE_HZ>> {
1756 self.command_signal.try_take()
1757 }
1758}
1759
1760#[doc(hidden)]
1762pub fn __audio_player_play<I, const MAX_CLIPS: usize, const SAMPLE_RATE_HZ: u32>(
1763 audio_player_static: &'static AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>,
1764 audio_clips: I,
1765 at_end: AtEnd,
1766) where
1767 I: IntoIterator<Item = &'static dyn Playable<SAMPLE_RATE_HZ>>,
1768{
1769 assert!(MAX_CLIPS > 0, "play disabled: max_clips is 0");
1770 let mut audio_clip_sequence: Vec<PlaybackClip<SAMPLE_RATE_HZ>, MAX_CLIPS> = Vec::new();
1771 for audio_clip in audio_clips {
1772 assert!(
1773 audio_clip_sequence
1774 .push(sealed::PlayableSealed::playback_clip(audio_clip))
1775 .is_ok(),
1776 "play sequence fits within max_clips"
1777 );
1778 }
1779 assert!(
1780 !audio_clip_sequence.is_empty(),
1781 "play requires at least one clip"
1782 );
1783
1784 audio_player_static.mark_pending_play();
1785 audio_player_static.signal(AudioCommand::Play {
1786 audio_clips: audio_clip_sequence,
1787 at_end,
1788 });
1789}
1790
1791#[doc(hidden)]
1793pub fn __audio_player_stop<const MAX_CLIPS: usize, const SAMPLE_RATE_HZ: u32>(
1794 audio_player_static: &'static AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>,
1795) {
1796 audio_player_static.signal(AudioCommand::Stop);
1797}
1798
1799#[doc(hidden)]
1801pub async fn __audio_player_wait_until_stopped<
1802 const MAX_CLIPS: usize,
1803 const SAMPLE_RATE_HZ: u32,
1804>(
1805 audio_player_static: &'static AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>,
1806) {
1807 audio_player_static.wait_until_stopped().await;
1808}
1809
1810#[doc(hidden)]
1812pub fn __audio_player_set_volume<const MAX_CLIPS: usize, const SAMPLE_RATE_HZ: u32>(
1813 audio_player_static: &'static AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>,
1814 volume: Volume,
1815) {
1816 audio_player_static.set_runtime_volume(volume);
1817}
1818
1819#[doc(hidden)]
1821#[must_use]
1822pub fn __audio_player_volume<const MAX_CLIPS: usize, const SAMPLE_RATE_HZ: u32>(
1823 audio_player_static: &'static AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>,
1824) -> Volume {
1825 audio_player_static.runtime_volume()
1826}
1827
1828#[must_use]
1833#[doc(hidden)]
1834pub const fn __tone_pcm_clip<const SAMPLE_RATE_HZ: u32, const SAMPLE_COUNT: usize>(
1835 frequency_hz: u32,
1836) -> PcmClipBuf<SAMPLE_RATE_HZ, SAMPLE_COUNT> {
1837 __tone_pcm_clip_with_duration::<SAMPLE_RATE_HZ, SAMPLE_COUNT>(
1838 frequency_hz,
1839 duration_for_sample_count(SAMPLE_COUNT, SAMPLE_RATE_HZ),
1840 )
1841}
1842
1843#[must_use]
1847#[doc(hidden)]
1848pub const fn __tone_pcm_clip_with_duration<const SAMPLE_RATE_HZ: u32, const SAMPLE_COUNT: usize>(
1849 frequency_hz: u32,
1850 duration: core::time::Duration,
1851) -> PcmClipBuf<SAMPLE_RATE_HZ, SAMPLE_COUNT> {
1852 assert!(SAMPLE_RATE_HZ > 0, "sample_rate_hz must be > 0");
1853 let mut samples = [0_i16; SAMPLE_COUNT];
1854 let phase_step_u64 = ((frequency_hz as u64) << 32) / SAMPLE_RATE_HZ as u64;
1855 let phase_step_u32 = phase_step_u64 as u32;
1856 let mut phase_u32 = 0_u32;
1857
1858 let mut sample_index = 0usize;
1859 while sample_index < SAMPLE_COUNT {
1861 samples[sample_index] = sine_sample_from_phase(phase_u32);
1862 phase_u32 = phase_u32.wrapping_add(phase_step_u32);
1863 sample_index += 1;
1864 }
1865
1866 let max_duration = Duration::from_millis(50);
1868 assert!(max_duration.as_secs() == 0, "50ms cap must be sub-second");
1869 let attack_release_duration = match (duration.as_secs(), duration.subsec_nanos()) {
1870 (0, nanos) if nanos / 4 < max_duration.subsec_nanos() => Duration::new(0, nanos / 4),
1871 (_, _) => max_duration,
1872 };
1873 PcmClip { samples }.with_attack_release(attack_release_duration, attack_release_duration)
1874}
1875
1876#[must_use]
1881#[doc(hidden)]
1882pub const fn __pcm_clip_from_samples<const SAMPLE_RATE_HZ: u32, const SAMPLE_COUNT: usize>(
1883 samples: [i16; SAMPLE_COUNT],
1884) -> PcmClipBuf<SAMPLE_RATE_HZ, SAMPLE_COUNT> {
1885 assert!(SAMPLE_RATE_HZ > 0, "sample_rate_hz must be > 0");
1886 PcmClip { samples }
1887}
1888
1889#[must_use]
1894#[doc(hidden)]
1895pub const fn __adpcm_clip_from_parts<const SAMPLE_RATE_HZ: u32, const DATA_LEN: usize>(
1896 block_align: u16,
1897 samples_per_block: u16,
1898 data: [u8; DATA_LEN],
1899) -> AdpcmClipBuf<SAMPLE_RATE_HZ, DATA_LEN> {
1900 AdpcmClip::new(block_align, samples_per_block, data)
1901}
1902
1903#[must_use]
1908#[doc(hidden)]
1909pub const fn __pcm_with_adpcm_block_align<
1910 const SAMPLE_RATE_HZ: u32,
1911 const SAMPLE_COUNT: usize,
1912 const DATA_LEN: usize,
1913>(
1914 source_pcm_clip: &PcmClipBuf<SAMPLE_RATE_HZ, SAMPLE_COUNT>,
1915 block_align: usize,
1916) -> AdpcmClipBuf<SAMPLE_RATE_HZ, DATA_LEN> {
1917 source_pcm_clip.with_adpcm_block_align::<DATA_LEN>(block_align)
1918}
1919
1920#[must_use]
1926#[doc(hidden)]
1927pub const fn __resample_pcm_clip<
1928 const SOURCE_HZ: u32,
1929 const SOURCE_COUNT: usize,
1930 const TARGET_HZ: u32,
1931 const TARGET_COUNT: usize,
1932>(
1933 source_pcm_clip: PcmClipBuf<SOURCE_HZ, SOURCE_COUNT>,
1934) -> PcmClipBuf<TARGET_HZ, TARGET_COUNT> {
1935 assert!(SOURCE_COUNT > 0, "source sample count must be > 0");
1936 assert!(TARGET_HZ > 0, "destination sample_rate_hz must be > 0");
1937 let expected_destination_sample_count =
1938 __resampled_sample_count(SOURCE_COUNT, SOURCE_HZ, TARGET_HZ);
1939 assert!(
1940 TARGET_COUNT == expected_destination_sample_count,
1941 "destination sample count must preserve duration"
1942 );
1943
1944 let source_samples = source_pcm_clip.samples;
1945 let mut resampled_samples = [0_i16; TARGET_COUNT];
1946 let mut sample_index = 0_usize;
1947
1948 while sample_index < TARGET_COUNT {
1950 let source_position_numerator_u128 = sample_index as u128 * SOURCE_HZ as u128;
1951 let source_index_u128 = source_position_numerator_u128 / TARGET_HZ as u128;
1952 let source_fraction_numerator_u128 = source_position_numerator_u128 % TARGET_HZ as u128;
1953 let source_index = source_index_u128 as usize;
1954
1955 resampled_samples[sample_index] = if source_index + 1 >= SOURCE_COUNT {
1956 source_samples[SOURCE_COUNT - 1]
1957 } else if source_fraction_numerator_u128 == 0 {
1958 source_samples[source_index]
1959 } else {
1960 let left_sample_i128 = source_samples[source_index] as i128;
1961 let right_sample_i128 = source_samples[source_index + 1] as i128;
1962 let sample_delta_i128 = right_sample_i128 - left_sample_i128;
1963 let denom_i128 = TARGET_HZ as i128;
1964 let numerator_i128 = sample_delta_i128 * source_fraction_numerator_u128 as i128;
1965 let rounded_i128 = if numerator_i128 >= 0 {
1966 (numerator_i128 + (denom_i128 / 2)) / denom_i128
1967 } else {
1968 (numerator_i128 - (denom_i128 / 2)) / denom_i128
1969 };
1970 clamp_i64_to_i16((left_sample_i128 + rounded_i128) as i64)
1971 };
1972
1973 sample_index += 1;
1974 }
1975
1976 PcmClip {
1977 samples: resampled_samples,
1978 }
1979}
1980
1981#[doc(hidden)]
1984pub const fn decode_adpcm_nibble_const(
1985 adpcm_nibble: u8,
1986 predictor_i32: &mut i32,
1987 step_index_i32: &mut i32,
1988) -> i16 {
1989 let step = ADPCM_STEP_TABLE[*step_index_i32 as usize];
1990 let mut delta = step >> 3;
1991
1992 if (adpcm_nibble & 0x01) != 0 {
1993 delta += step >> 2;
1994 }
1995 if (adpcm_nibble & 0x02) != 0 {
1996 delta += step >> 1;
1997 }
1998 if (adpcm_nibble & 0x04) != 0 {
1999 delta += step;
2000 }
2001
2002 if (adpcm_nibble & 0x08) != 0 {
2003 *predictor_i32 -= delta;
2004 } else {
2005 *predictor_i32 += delta;
2006 }
2007
2008 if *predictor_i32 < i16::MIN as i32 {
2009 *predictor_i32 = i16::MIN as i32;
2010 } else if *predictor_i32 > i16::MAX as i32 {
2011 *predictor_i32 = i16::MAX as i32;
2012 }
2013 *step_index_i32 += ADPCM_INDEX_TABLE[adpcm_nibble as usize];
2014 if *step_index_i32 < 0 {
2015 *step_index_i32 = 0;
2016 } else if *step_index_i32 > 88 {
2017 *step_index_i32 = 88;
2018 }
2019
2020 *predictor_i32 as i16
2021}
2022
2023const fn encode_adpcm_nibble(
2024 target_sample_i32: i32,
2025 predictor_i32: &mut i32,
2026 step_index_i32: &mut i32,
2027) -> u8 {
2028 let step = ADPCM_STEP_TABLE[*step_index_i32 as usize];
2029 let mut diff = target_sample_i32 - *predictor_i32;
2030 let mut adpcm_nibble = 0_u8;
2031 if diff < 0 {
2032 adpcm_nibble |= 0x08;
2033 diff = -diff;
2034 }
2035
2036 let mut delta = step >> 3;
2037 if diff >= step {
2038 adpcm_nibble |= 0x04;
2039 diff -= step;
2040 delta += step;
2041 }
2042 if diff >= (step >> 1) {
2043 adpcm_nibble |= 0x02;
2044 diff -= step >> 1;
2045 delta += step >> 1;
2046 }
2047 if diff >= (step >> 2) {
2048 adpcm_nibble |= 0x01;
2049 delta += step >> 2;
2050 }
2051
2052 if (adpcm_nibble & 0x08) != 0 {
2053 *predictor_i32 -= delta;
2054 } else {
2055 *predictor_i32 += delta;
2056 }
2057
2058 if *predictor_i32 < i16::MIN as i32 {
2059 *predictor_i32 = i16::MIN as i32;
2060 } else if *predictor_i32 > i16::MAX as i32 {
2061 *predictor_i32 = i16::MAX as i32;
2062 }
2063 *step_index_i32 += ADPCM_INDEX_TABLE[adpcm_nibble as usize];
2064 if *step_index_i32 < 0 {
2065 *step_index_i32 = 0;
2066 } else if *step_index_i32 > 88 {
2067 *step_index_i32 = 88;
2068 }
2069
2070 adpcm_nibble
2071}
2072
2073#[doc(hidden)]
2078#[macro_export]
2079macro_rules! pcm_clip {
2080 ($($tt:tt)*) => { $crate::__audio_clip_parse! { $($tt)* } };
2084}
2085
2086#[doc(hidden)]
2087#[macro_export]
2088macro_rules! __audio_clip_parse {
2089 (
2090 $vis:vis $name:ident {
2091 file: $file:expr,
2092 sample_rate_hz: $source_sample_rate_hz:expr,
2093 target_sample_rate_hz: $target_sample_rate_hz:expr $(,)?
2094 }
2095 ) => {
2096 $crate::__audio_clip_dispatch! {
2097 vis: $vis,
2098 name: $name,
2099 file: $file,
2100 source_sample_rate_hz: $source_sample_rate_hz,
2101 target_sample_rate_hz: $target_sample_rate_hz,
2102 }
2103 };
2104 (
2105 $vis:vis $name:ident {
2106 file: $file:expr,
2107 sample_rate_hz: $source_sample_rate_hz:expr,
2108 target_sample_rate_hz: $target_sample_rate_hz:expr,
2109 $(,)?
2110 }
2111 ) => {
2112 $crate::__audio_clip_dispatch! {
2113 vis: $vis,
2114 name: $name,
2115 file: $file,
2116 source_sample_rate_hz: $source_sample_rate_hz,
2117 target_sample_rate_hz: $target_sample_rate_hz,
2118 }
2119 };
2120 (
2121 $vis:vis $name:ident {
2122 file: $file:expr,
2123 sample_rate_hz: $sample_rate_hz:expr $(,)?
2124 }
2125 ) => {
2126 $crate::__audio_clip_dispatch! {
2127 vis: $vis,
2128 name: $name,
2129 file: $file,
2130 source_sample_rate_hz: $sample_rate_hz,
2131 target_sample_rate_hz: $sample_rate_hz,
2132 }
2133 };
2134 (
2135 $vis:vis $name:ident {
2136 file: $file:expr,
2137 sample_rate_hz: $sample_rate_hz:expr,
2138 $(,)?
2139 }
2140 ) => {
2141 $crate::__audio_clip_dispatch! {
2142 vis: $vis,
2143 name: $name,
2144 file: $file,
2145 source_sample_rate_hz: $sample_rate_hz,
2146 target_sample_rate_hz: $sample_rate_hz,
2147 }
2148 };
2149 (
2150 $vis:vis $name:ident {
2151 file: $file:expr,
2152 sample_rate_hz: $sample_rate_hz:expr,
2153 $(,)?
2154 }
2155 ) => {
2156 $crate::__audio_clip_dispatch! {
2157 vis: $vis,
2158 name: $name,
2159 file: $file,
2160 source_sample_rate_hz: $sample_rate_hz,
2161 target_sample_rate_hz: $sample_rate_hz,
2162 }
2163 };
2164 (
2166 $vis:vis $name:ident {
2167 file: $file:expr,
2168 source_sample_rate_hz: $source_sample_rate_hz:expr,
2169 target_sample_rate_hz: $target_sample_rate_hz:expr $(,)?
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 source_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
2196#[doc(hidden)]
2197#[macro_export]
2198macro_rules! __audio_clip_dispatch {
2199 (
2200 vis: $vis:vis,
2201 name: $name:ident,
2202 file: $file:expr,
2203 source_sample_rate_hz: $source_sample_rate_hz:expr,
2204 target_sample_rate_hz: $target_sample_rate_hz:expr $(,)?
2205 ) => {
2206 $crate::__audio_clip_impl! {
2207 vis: $vis,
2208 name: $name,
2209 file: $file,
2210 source_sample_rate_hz: $source_sample_rate_hz,
2211 target_sample_rate_hz: $target_sample_rate_hz,
2212 }
2213 };
2214}
2215
2216#[doc(hidden)]
2217#[macro_export]
2218macro_rules! __audio_clip_impl {
2219 (
2220 vis: $vis:vis,
2221 name: $name:ident,
2222 file: $file:expr,
2223 source_sample_rate_hz: $source_sample_rate_hz:expr,
2224 target_sample_rate_hz: $target_sample_rate_hz:expr $(,)?
2225 ) => {
2226 $crate::__paste! {
2227 const [<$name:upper _SOURCE_SAMPLE_RATE_HZ>]: u32 = $source_sample_rate_hz;
2228 const [<$name:upper _TARGET_SAMPLE_RATE_HZ>]: u32 = $target_sample_rate_hz;
2229
2230 #[allow(non_snake_case)]
2231 #[doc = concat!(
2232 "Audio clip module generated by [`pcm_clip!`](macro@crate::audio_player::pcm_clip).\n\n",
2233 "[`SAMPLE_RATE_HZ`](Self::SAMPLE_RATE_HZ), ",
2234 "[`PCM_SAMPLE_COUNT`](Self::PCM_SAMPLE_COUNT), ",
2235 "[`ADPCM_DATA_LEN`](Self::ADPCM_DATA_LEN), ",
2236 "[`pcm_clip`](Self::pcm_clip), ",
2237 "and [`adpcm_clip`](Self::adpcm_clip)."
2238 )]
2239 $vis mod $name {
2240 const SOURCE_SAMPLE_RATE_HZ: u32 = super::[<$name:upper _SOURCE_SAMPLE_RATE_HZ>];
2243 const TARGET_SAMPLE_RATE_HZ: u32 = super::[<$name:upper _TARGET_SAMPLE_RATE_HZ>];
2244 #[doc = "Sample rate in hertz for this generated clip output."]
2245 pub const SAMPLE_RATE_HZ: u32 = TARGET_SAMPLE_RATE_HZ;
2246 const AUDIO_SAMPLE_BYTES_LEN: usize = include_bytes!($file).len();
2247 const SOURCE_SAMPLE_COUNT: usize = AUDIO_SAMPLE_BYTES_LEN / 2;
2248 #[doc = "Number of samples for uncompressed (PCM) version of this clip."]
2249 pub const PCM_SAMPLE_COUNT: usize = $crate::audio_player::__resampled_sample_count(
2250 SOURCE_SAMPLE_COUNT,
2251 SOURCE_SAMPLE_RATE_HZ,
2252 TARGET_SAMPLE_RATE_HZ,
2253 );
2254 #[doc = "Byte length for compressed (ADPCM) encoding this clip."]
2255 pub const ADPCM_DATA_LEN: usize =
2256 $crate::audio_player::__adpcm_data_len_for_pcm_samples(PCM_SAMPLE_COUNT);
2257
2258 #[allow(dead_code)]
2259 type SourcePcmClip = $crate::audio_player::PcmClipBuf<
2260 { SOURCE_SAMPLE_RATE_HZ },
2261 { SOURCE_SAMPLE_COUNT },
2262 >;
2263
2264 #[doc = "`const` function that returns the uncompressed (PCM) version of this clip."]
2265 #[must_use]
2266 pub const fn pcm_clip() -> $crate::audio_player::PcmClipBuf<
2267 { SAMPLE_RATE_HZ },
2268 { PCM_SAMPLE_COUNT },
2269 > {
2270 assert!(
2271 AUDIO_SAMPLE_BYTES_LEN % 2 == 0,
2272 "audio byte length must be even for s16le"
2273 );
2274
2275 let audio_sample_s16le: &[u8; AUDIO_SAMPLE_BYTES_LEN] = include_bytes!($file);
2276 let mut samples = [0_i16; SOURCE_SAMPLE_COUNT];
2277 let mut sample_index = 0_usize;
2278 while sample_index < SOURCE_SAMPLE_COUNT {
2279 let byte_index = sample_index * 2;
2280 samples[sample_index] = i16::from_le_bytes([
2281 audio_sample_s16le[byte_index],
2282 audio_sample_s16le[byte_index + 1],
2283 ]);
2284 sample_index += 1;
2285 }
2286 $crate::audio_player::__resample_pcm_clip::<
2287 SOURCE_SAMPLE_RATE_HZ,
2288 SOURCE_SAMPLE_COUNT,
2289 TARGET_SAMPLE_RATE_HZ,
2290 PCM_SAMPLE_COUNT,
2291 >($crate::audio_player::__pcm_clip_from_samples::<
2292 SOURCE_SAMPLE_RATE_HZ,
2293 SOURCE_SAMPLE_COUNT,
2294 >(samples))
2295 }
2296
2297 #[doc = "`const` function that returns the compressed (ADPCM) encoding for this clip."]
2298 #[must_use]
2299 pub const fn adpcm_clip() -> $crate::audio_player::AdpcmClipBuf<
2300 { SAMPLE_RATE_HZ },
2301 { ADPCM_DATA_LEN },
2302 > {
2303 pcm_clip().with_adpcm::<ADPCM_DATA_LEN>()
2304 }
2305
2306 }
2307 }
2308 };
2309}
2310
2311#[doc = "Macro to \"compile in\" a compressed (ADPCM) WAV clip from an external file (includes syntax details)."]
2312#[doc = include_str!("audio_player/adpcm_clip_docs.md")]
2313#[doc = include_str!("audio_player/audio_prep_steps_1_2.md")]
2314#[doc = include_str!("audio_player/adpcm_clip_step_3.md")]
2315#[doc(inline)]
2316pub use crate::adpcm_clip;
2317
2318#[doc(hidden)]
2319#[macro_export]
2320macro_rules! adpcm_clip {
2321 ($($tt:tt)*) => { $crate::__adpcm_clip_parse! { $($tt)* } };
2322}
2323
2324#[doc(hidden)]
2325#[macro_export]
2326macro_rules! __adpcm_clip_parse {
2327 (
2328 $vis:vis $name:ident {
2329 file: $file:expr,
2330 target_sample_rate_hz: $target_sample_rate_hz:expr $(,)?
2331 }
2332 ) => {
2333 $crate::__paste! {
2334 const [<$name:upper _TARGET_SAMPLE_RATE_HZ>]: u32 = $target_sample_rate_hz;
2335
2336 #[allow(non_snake_case)]
2337 #[allow(missing_docs)]
2338 $vis mod $name {
2339 const PARSED_WAV: $crate::audio_player::ParsedAdpcmWavHeader =
2340 $crate::audio_player::__parse_adpcm_wav_header(include_bytes!($file));
2341 const SOURCE_SAMPLE_RATE_HZ: u32 = PARSED_WAV.sample_rate_hz;
2342 const TARGET_SAMPLE_RATE_HZ: u32 = super::[<$name:upper _TARGET_SAMPLE_RATE_HZ>];
2343 pub const SAMPLE_RATE_HZ: u32 = TARGET_SAMPLE_RATE_HZ;
2344
2345 const SOURCE_SAMPLE_COUNT: usize = PARSED_WAV.sample_count;
2346 #[doc = "Number of samples for uncompressed (PCM) version of this clip."]
2347 pub const PCM_SAMPLE_COUNT: usize = $crate::audio_player::__resampled_sample_count(
2348 SOURCE_SAMPLE_COUNT,
2349 SOURCE_SAMPLE_RATE_HZ,
2350 TARGET_SAMPLE_RATE_HZ,
2351 );
2352 const BLOCK_ALIGN: usize = PARSED_WAV.block_align;
2353 const SOURCE_DATA_LEN: usize = PARSED_WAV.data_chunk_len;
2354 #[doc = "Byte length for compressed (ADPCM) encoding this clip."]
2355 pub const ADPCM_DATA_LEN: usize = if TARGET_SAMPLE_RATE_HZ == SOURCE_SAMPLE_RATE_HZ {
2356 SOURCE_DATA_LEN
2357 } else {
2358 $crate::audio_player::__adpcm_data_len_for_pcm_samples_with_block_align(
2359 PCM_SAMPLE_COUNT,
2360 BLOCK_ALIGN,
2361 )
2362 };
2363 type SourceAdpcmClip = $crate::audio_player::AdpcmClipBuf<SOURCE_SAMPLE_RATE_HZ, SOURCE_DATA_LEN>;
2364
2365 #[must_use]
2366 const fn source_adpcm_clip() -> SourceAdpcmClip {
2367 let wav_bytes = include_bytes!($file);
2368 let parsed_wav = $crate::audio_player::__parse_adpcm_wav_header(wav_bytes);
2369 assert!(parsed_wav.block_align <= u16::MAX as usize, "block_align too large");
2370 assert!(
2371 parsed_wav.samples_per_block <= u16::MAX as usize,
2372 "samples_per_block too large"
2373 );
2374
2375 let mut adpcm_data = [0_u8; SOURCE_DATA_LEN];
2376 let mut data_index = 0usize;
2377 while data_index < SOURCE_DATA_LEN {
2378 adpcm_data[data_index] = wav_bytes[parsed_wav.data_chunk_start + data_index];
2379 data_index += 1;
2380 }
2381
2382 $crate::audio_player::__adpcm_clip_from_parts(
2383 parsed_wav.block_align as u16,
2384 parsed_wav.samples_per_block as u16,
2385 adpcm_data,
2386 )
2387 }
2388
2389 #[doc = "`const` function that returns the uncompressed (PCM) version of this clip."]
2390 #[must_use]
2391 pub const fn pcm_clip() -> $crate::audio_player::PcmClipBuf<SAMPLE_RATE_HZ, PCM_SAMPLE_COUNT> {
2392 $crate::audio_player::__resample_pcm_clip::<
2393 SOURCE_SAMPLE_RATE_HZ,
2394 SOURCE_SAMPLE_COUNT,
2395 TARGET_SAMPLE_RATE_HZ,
2396 PCM_SAMPLE_COUNT,
2397 >(source_adpcm_clip().with_pcm::<SOURCE_SAMPLE_COUNT>())
2398 }
2399
2400 #[doc = "`const` function that returns the compressed (ADPCM) encoding for this clip."]
2401 #[must_use]
2402 pub const fn adpcm_clip() -> $crate::audio_player::AdpcmClipBuf<SAMPLE_RATE_HZ, ADPCM_DATA_LEN> {
2403 if TARGET_SAMPLE_RATE_HZ == SOURCE_SAMPLE_RATE_HZ {
2404 let wav_bytes = include_bytes!($file);
2405 let parsed_wav = $crate::audio_player::__parse_adpcm_wav_header(wav_bytes);
2406 assert!(parsed_wav.block_align <= u16::MAX as usize, "block_align too large");
2407 assert!(
2408 parsed_wav.samples_per_block <= u16::MAX as usize,
2409 "samples_per_block too large"
2410 );
2411 let mut adpcm_data = [0_u8; ADPCM_DATA_LEN];
2412 let mut data_index = 0usize;
2413 while data_index < ADPCM_DATA_LEN {
2414 adpcm_data[data_index] =
2415 wav_bytes[parsed_wav.data_chunk_start + data_index];
2416 data_index += 1;
2417 }
2418 $crate::audio_player::__adpcm_clip_from_parts(
2419 parsed_wav.block_align as u16,
2420 parsed_wav.samples_per_block as u16,
2421 adpcm_data,
2422 )
2423 } else {
2424 $crate::audio_player::__pcm_with_adpcm_block_align::<
2425 SAMPLE_RATE_HZ,
2426 PCM_SAMPLE_COUNT,
2427 ADPCM_DATA_LEN,
2428 >(&pcm_clip(), BLOCK_ALIGN)
2429 }
2430 }
2431
2432 }
2433 }
2434 };
2435
2436 (
2437 $vis:vis $name:ident {
2438 file: $file:expr $(,)?
2439 }
2440 ) => {
2441 $crate::__adpcm_clip_parse! {
2442 $vis $name {
2443 file: $file,
2444 target_sample_rate_hz: $crate::audio_player::__parse_adpcm_wav_header(include_bytes!($file)).sample_rate_hz,
2445 }
2446 }
2447 };
2448}
2449
2450#[doc(hidden)]
2462#[macro_export]
2463macro_rules! tone {
2464 ($frequency_hz:expr, $sample_rate_hz:expr, $duration:expr) => {
2465 $crate::audio_player::__tone_pcm_clip_with_duration::<
2466 { $sample_rate_hz },
2467 { $crate::audio_player::__samples_for_duration($duration, $sample_rate_hz) },
2468 >($frequency_hz, $duration)
2469 };
2470}
2471
2472#[doc = "Macro to \"compile in\" an uncompressed (PCM) clip from an external file (includes syntax details)."]
2473#[doc = include_str!("audio_player/pcm_clip_docs.md")]
2474#[doc = include_str!("audio_player/audio_prep_steps_1_2.md")]
2475#[doc = include_str!("audio_player/pcm_clip_step_3.md")]
2476#[doc(inline)]
2477pub use crate::pcm_clip;
2478#[doc(inline)]
2479pub use crate::tone;