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 % block_align as usize == 0,
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 % block_align == 0,
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 % block_align == 0,
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 % block_align != 0 {
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)]
1146pub trait Playable<const SAMPLE_RATE_HZ: u32>: sealed::PlayableSealed<SAMPLE_RATE_HZ> {}
1147
1148impl<const SAMPLE_RATE_HZ: u32, T: ?Sized> Playable<SAMPLE_RATE_HZ> for T where
1149 T: sealed::PlayableSealed<SAMPLE_RATE_HZ>
1150{
1151}
1152
1153#[allow(async_fn_in_trait)]
1381pub trait AudioPlayer<const SAMPLE_RATE_HZ: u32> {
1382 const SAMPLE_RATE_HZ: u32;
1384 const MAX_CLIPS: usize;
1386 const INITIAL_VOLUME: Volume;
1388 const MAX_VOLUME: Volume;
1390
1391 fn play<I>(&self, audio_clips: I, at_end: AtEnd)
1398 where
1399 I: IntoIterator<Item = &'static dyn Playable<SAMPLE_RATE_HZ>>;
1400
1401 fn stop(&self);
1405
1406 async fn wait_until_stopped(&self);
1410
1411 fn set_volume(&self, volume: Volume);
1415
1416 fn volume(&self) -> Volume;
1420}
1421
1422mod sealed {
1423 use super::{AdpcmClip, PcmClip, PlaybackClip, SilenceClip};
1424
1425 pub(crate) trait PlayableSealed<const SAMPLE_RATE_HZ: u32> {
1426 fn playback_clip(&'static self) -> PlaybackClip<SAMPLE_RATE_HZ>;
1427 }
1428
1429 impl<const SAMPLE_RATE_HZ: u32> PlayableSealed<SAMPLE_RATE_HZ> for PcmClip<SAMPLE_RATE_HZ> {
1430 fn playback_clip(&'static self) -> PlaybackClip<SAMPLE_RATE_HZ> {
1431 PlaybackClip::Pcm(self)
1432 }
1433 }
1434
1435 impl<const SAMPLE_RATE_HZ: u32, const SAMPLE_COUNT: usize> PlayableSealed<SAMPLE_RATE_HZ>
1436 for PcmClip<SAMPLE_RATE_HZ, [i16; SAMPLE_COUNT]>
1437 {
1438 fn playback_clip(&'static self) -> PlaybackClip<SAMPLE_RATE_HZ> {
1439 PlaybackClip::Pcm(self)
1440 }
1441 }
1442
1443 impl<const SAMPLE_RATE_HZ: u32> PlayableSealed<SAMPLE_RATE_HZ> for AdpcmClip<SAMPLE_RATE_HZ> {
1444 fn playback_clip(&'static self) -> PlaybackClip<SAMPLE_RATE_HZ> {
1445 PlaybackClip::Adpcm(self)
1446 }
1447 }
1448
1449 impl<const SAMPLE_RATE_HZ: u32, const DATA_LEN: usize> PlayableSealed<SAMPLE_RATE_HZ>
1450 for AdpcmClip<SAMPLE_RATE_HZ, [u8; DATA_LEN]>
1451 {
1452 fn playback_clip(&'static self) -> PlaybackClip<SAMPLE_RATE_HZ> {
1453 PlaybackClip::Adpcm(self)
1454 }
1455 }
1456
1457 impl<const SAMPLE_RATE_HZ: u32> PlayableSealed<SAMPLE_RATE_HZ> for SilenceClip {
1458 fn playback_clip(&'static self) -> PlaybackClip<SAMPLE_RATE_HZ> {
1459 PlaybackClip::Silence(self.duration())
1460 }
1461 }
1462}
1463
1464pub struct PcmClip<const SAMPLE_RATE_HZ: u32, T: ?Sized = [i16]> {
1471 samples: T,
1472}
1473
1474impl<const SAMPLE_RATE_HZ: u32, T: ?Sized> PcmClip<SAMPLE_RATE_HZ, T> {
1475 #[must_use]
1477 pub fn samples(&self) -> &T {
1478 &self.samples
1479 }
1480}
1481
1482pub type PcmClipBuf<const SAMPLE_RATE_HZ: u32, const SAMPLE_COUNT: usize> =
1492 PcmClip<SAMPLE_RATE_HZ, [i16; SAMPLE_COUNT]>;
1493
1494impl<const SAMPLE_RATE_HZ: u32, const SAMPLE_COUNT: usize>
1500 PcmClip<SAMPLE_RATE_HZ, [i16; SAMPLE_COUNT]>
1501{
1502 #[must_use]
1513 pub const fn with_gain(self, gain: Gain) -> Self {
1514 assert!(SAMPLE_RATE_HZ > 0, "sample_rate_hz must be > 0");
1515 let mut scaled_samples = [0_i16; SAMPLE_COUNT];
1516 let mut sample_index = 0_usize;
1517 while sample_index < SAMPLE_COUNT {
1519 scaled_samples[sample_index] =
1520 scale_sample_with_linear(self.samples[sample_index], gain.linear());
1521 sample_index += 1;
1522 }
1523 Self {
1524 samples: scaled_samples,
1525 }
1526 }
1527
1528 #[must_use]
1536 pub(crate) const fn with_attack_release(self, attack: Duration, release: Duration) -> Self {
1537 assert!(SAMPLE_RATE_HZ > 0, "sample_rate_hz must be > 0");
1538 let attack_sample_count = __samples_for_duration(attack, SAMPLE_RATE_HZ);
1539 let release_sample_count = __samples_for_duration(release, SAMPLE_RATE_HZ);
1540 self.with_attack_release_sample_count(attack_sample_count, release_sample_count)
1541 }
1542
1543 #[must_use]
1544 const fn with_attack_release_sample_count(
1545 self,
1546 attack_sample_count: usize,
1547 release_sample_count: usize,
1548 ) -> Self {
1549 assert!(
1550 attack_sample_count <= SAMPLE_COUNT,
1551 "attack duration must fit within clip duration"
1552 );
1553 assert!(
1554 release_sample_count <= SAMPLE_COUNT,
1555 "release duration must fit within clip duration"
1556 );
1557 assert!(
1558 attack_sample_count + release_sample_count <= SAMPLE_COUNT,
1559 "attack + release must fit within clip duration"
1560 );
1561
1562 let mut shaped_samples = self.samples;
1563
1564 if attack_sample_count > 0 {
1565 let attack_sample_count_i32 = attack_sample_count as i32;
1566 let mut sample_index = 0usize;
1567 while sample_index < attack_sample_count {
1569 let envelope_numerator_i32 = sample_index as i32;
1570 shaped_samples[sample_index] = scale_sample_with_linear(
1571 shaped_samples[sample_index],
1572 (envelope_numerator_i32 * i16::MAX as i32) / attack_sample_count_i32,
1573 );
1574 sample_index += 1;
1575 }
1576 }
1577
1578 if release_sample_count > 0 {
1579 let release_sample_count_i32 = release_sample_count as i32;
1580 let release_start_index = SAMPLE_COUNT - release_sample_count;
1581 let mut release_index = 0usize;
1582 while release_index < release_sample_count {
1584 let sample_index = release_start_index + release_index;
1585 let envelope_numerator_i32 = (release_sample_count - release_index) as i32;
1586 shaped_samples[sample_index] = scale_sample_with_linear(
1587 shaped_samples[sample_index],
1588 (envelope_numerator_i32 * i16::MAX as i32) / release_sample_count_i32,
1589 );
1590 release_index += 1;
1591 }
1592 }
1593
1594 Self {
1595 samples: shaped_samples,
1596 }
1597 }
1598
1599 #[must_use]
1604 pub const fn with_adpcm<const DATA_LEN: usize>(
1605 &self,
1606 ) -> AdpcmClipBuf<SAMPLE_RATE_HZ, DATA_LEN> {
1607 self.with_adpcm_block_align::<DATA_LEN>(ADPCM_ENCODE_BLOCK_ALIGN)
1608 }
1609
1610 #[must_use]
1611 pub(crate) const fn with_adpcm_block_align<const DATA_LEN: usize>(
1612 &self,
1613 block_align: usize,
1614 ) -> AdpcmClipBuf<SAMPLE_RATE_HZ, DATA_LEN> {
1615 assert!(block_align >= 5, "block_align must be >= 5");
1616 assert!(
1617 block_align <= u16::MAX as usize,
1618 "block_align must fit in u16"
1619 );
1620 let samples_per_block = __adpcm_samples_per_block(block_align);
1621 assert!(
1622 samples_per_block <= u16::MAX as usize,
1623 "samples_per_block must fit in u16"
1624 );
1625 assert!(
1626 DATA_LEN
1627 == __adpcm_data_len_for_pcm_samples_with_block_align(SAMPLE_COUNT, block_align),
1628 "adpcm data length must match sample count and block_align"
1629 );
1630 if SAMPLE_COUNT == 0 {
1631 return AdpcmClip::new(
1632 block_align as u16,
1633 samples_per_block as u16,
1634 SAMPLE_COUNT,
1635 [0; DATA_LEN],
1636 );
1637 }
1638
1639 let mut adpcm_data = [0_u8; DATA_LEN];
1640 let mut sample_index = 0usize;
1641 let mut data_index = 0usize;
1642 let payload_len_per_block = block_align - 4;
1643
1644 while sample_index < SAMPLE_COUNT {
1646 let mut predictor_i32 = self.samples[sample_index] as i32;
1647 let mut step_index_i32 = 0_i32;
1648
1649 let predictor_i16 = predictor_i32 as i16;
1650 let predictor_bytes = predictor_i16.to_le_bytes();
1651 adpcm_data[data_index] = predictor_bytes[0];
1652 adpcm_data[data_index + 1] = predictor_bytes[1];
1653 adpcm_data[data_index + 2] = step_index_i32 as u8;
1654 adpcm_data[data_index + 3] = 0;
1655 data_index += 4;
1656 sample_index += 1;
1657
1658 let mut payload_byte_index = 0usize;
1659 while payload_byte_index < payload_len_per_block {
1661 let mut adpcm_byte = 0_u8;
1662
1663 let mut nibble_index = 0usize;
1664 while nibble_index < 2 {
1666 let target_sample_i32 = if sample_index < SAMPLE_COUNT {
1667 self.samples[sample_index] as i32
1668 } else {
1669 predictor_i32
1670 };
1671 let adpcm_nibble = encode_adpcm_nibble(
1672 target_sample_i32,
1673 &mut predictor_i32,
1674 &mut step_index_i32,
1675 );
1676 adpcm_byte |= adpcm_nibble << (nibble_index * 4);
1677 sample_index += 1;
1678 nibble_index += 1;
1679 }
1680
1681 adpcm_data[data_index] = adpcm_byte;
1682 data_index += 1;
1683 payload_byte_index += 1;
1684 }
1685 }
1686
1687 AdpcmClip::new(
1688 block_align as u16,
1689 samples_per_block as u16,
1690 SAMPLE_COUNT,
1691 adpcm_data,
1692 )
1693 }
1694}
1695
1696#[doc(hidden)]
1699pub enum AudioCommand<const MAX_CLIPS: usize, const SAMPLE_RATE_HZ: u32> {
1700 Play {
1701 audio_clips: Vec<PlaybackClip<SAMPLE_RATE_HZ>, MAX_CLIPS>,
1702 at_end: AtEnd,
1703 },
1704 Stop,
1705}
1706
1707#[doc(hidden)]
1710pub struct AudioPlayerStatic<const MAX_CLIPS: usize, const SAMPLE_RATE_HZ: u32> {
1711 command_signal: Signal<CriticalSectionRawMutex, AudioCommand<MAX_CLIPS, SAMPLE_RATE_HZ>>,
1712 stopped_signal: Signal<CriticalSectionRawMutex, ()>,
1713 is_playing: AtomicBool,
1714 has_pending_play: AtomicBool,
1715 max_volume_linear: i32,
1716 runtime_volume_relative_linear: AtomicI32,
1717}
1718
1719impl<const MAX_CLIPS: usize, const SAMPLE_RATE_HZ: u32>
1720 AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>
1721{
1722 #[must_use]
1724 pub const fn new_static() -> Self {
1725 Self::new_static_with_max_volume_and_initial_volume(Volume::MAX, Volume::MAX)
1726 }
1727
1728 #[must_use]
1730 pub const fn new_static_with_max_volume(max_volume: Volume) -> Self {
1731 Self::new_static_with_max_volume_and_initial_volume(max_volume, Volume::MAX)
1732 }
1733
1734 #[must_use]
1737 pub const fn new_static_with_max_volume_and_initial_volume(
1738 max_volume: Volume,
1739 initial_volume: Volume,
1740 ) -> Self {
1741 Self {
1742 command_signal: Signal::new(),
1743 stopped_signal: Signal::new(),
1744 is_playing: AtomicBool::new(false),
1745 has_pending_play: AtomicBool::new(false),
1746 max_volume_linear: max_volume.to_i16() as i32,
1747 runtime_volume_relative_linear: AtomicI32::new(initial_volume.to_i16() as i32),
1748 }
1749 }
1750
1751 fn signal(&self, audio_command: AudioCommand<MAX_CLIPS, SAMPLE_RATE_HZ>) {
1752 self.command_signal.signal(audio_command);
1753 }
1754
1755 fn mark_pending_play(&self) {
1756 self.has_pending_play.store(true, AtomicOrdering::Relaxed);
1757 }
1758
1759 #[doc(hidden)]
1761 pub fn mark_playing(&self) {
1762 self.has_pending_play.store(false, AtomicOrdering::Relaxed);
1763 self.is_playing.store(true, AtomicOrdering::Relaxed);
1764 }
1765
1766 #[doc(hidden)]
1768 pub fn mark_stopped(&self) {
1769 self.has_pending_play.store(false, AtomicOrdering::Relaxed);
1770 self.is_playing.store(false, AtomicOrdering::Relaxed);
1771 self.stopped_signal.signal(());
1772 }
1773
1774 fn is_idle(&self) -> bool {
1775 !self.has_pending_play.load(AtomicOrdering::Relaxed)
1776 && !self.is_playing.load(AtomicOrdering::Relaxed)
1777 }
1778
1779 async fn wait_until_stopped(&self) {
1780 while !self.is_idle() {
1781 self.stopped_signal.wait().await;
1782 }
1783 }
1784
1785 fn set_runtime_volume(&self, volume: Volume) {
1786 self.runtime_volume_relative_linear
1787 .store(volume.to_i16() as i32, Ordering::Relaxed);
1788 }
1789
1790 fn runtime_volume(&self) -> Volume {
1791 Volume::from_i16(self.runtime_volume_relative_linear.load(Ordering::Relaxed) as i16)
1792 }
1793
1794 #[doc(hidden)]
1797 pub fn effective_runtime_volume(&self) -> Volume {
1798 let runtime_volume_relative = self.runtime_volume();
1799 Volume::from_i16(scale_linear(self.max_volume_linear, runtime_volume_relative) as i16)
1800 }
1801
1802 #[doc(hidden)]
1804 pub async fn wait(&self) -> AudioCommand<MAX_CLIPS, SAMPLE_RATE_HZ> {
1805 self.command_signal.wait().await
1806 }
1807
1808 #[doc(hidden)]
1811 pub fn try_take_command(&self) -> Option<AudioCommand<MAX_CLIPS, SAMPLE_RATE_HZ>> {
1812 self.command_signal.try_take()
1813 }
1814}
1815
1816#[doc(hidden)]
1818pub fn __audio_player_play<I, const MAX_CLIPS: usize, const SAMPLE_RATE_HZ: u32>(
1819 audio_player_static: &'static AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>,
1820 audio_clips: I,
1821 at_end: AtEnd,
1822) where
1823 I: IntoIterator<Item = &'static dyn Playable<SAMPLE_RATE_HZ>>,
1824{
1825 assert!(MAX_CLIPS > 0, "play disabled: max_clips is 0");
1826 let mut audio_clip_sequence: Vec<PlaybackClip<SAMPLE_RATE_HZ>, MAX_CLIPS> = Vec::new();
1827 for audio_clip in audio_clips {
1828 assert!(
1829 audio_clip_sequence
1830 .push(sealed::PlayableSealed::playback_clip(audio_clip))
1831 .is_ok(),
1832 "play sequence fits within max_clips"
1833 );
1834 }
1835 assert!(
1836 !audio_clip_sequence.is_empty(),
1837 "play requires at least one clip"
1838 );
1839
1840 audio_player_static.mark_pending_play();
1841 audio_player_static.signal(AudioCommand::Play {
1842 audio_clips: audio_clip_sequence,
1843 at_end,
1844 });
1845}
1846
1847#[doc(hidden)]
1849pub fn __audio_player_stop<const MAX_CLIPS: usize, const SAMPLE_RATE_HZ: u32>(
1850 audio_player_static: &'static AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>,
1851) {
1852 audio_player_static.signal(AudioCommand::Stop);
1853}
1854
1855#[doc(hidden)]
1857pub async fn __audio_player_wait_until_stopped<
1858 const MAX_CLIPS: usize,
1859 const SAMPLE_RATE_HZ: u32,
1860>(
1861 audio_player_static: &'static AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>,
1862) {
1863 audio_player_static.wait_until_stopped().await;
1864}
1865
1866#[doc(hidden)]
1868pub fn __audio_player_set_volume<const MAX_CLIPS: usize, const SAMPLE_RATE_HZ: u32>(
1869 audio_player_static: &'static AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>,
1870 volume: Volume,
1871) {
1872 audio_player_static.set_runtime_volume(volume);
1873}
1874
1875#[doc(hidden)]
1877#[must_use]
1878pub fn __audio_player_volume<const MAX_CLIPS: usize, const SAMPLE_RATE_HZ: u32>(
1879 audio_player_static: &'static AudioPlayerStatic<MAX_CLIPS, SAMPLE_RATE_HZ>,
1880) -> Volume {
1881 audio_player_static.runtime_volume()
1882}
1883
1884#[must_use]
1889#[doc(hidden)]
1890pub const fn __tone_pcm_clip<const SAMPLE_RATE_HZ: u32, const SAMPLE_COUNT: usize>(
1891 frequency_hz: u32,
1892) -> PcmClipBuf<SAMPLE_RATE_HZ, SAMPLE_COUNT> {
1893 __tone_pcm_clip_with_duration::<SAMPLE_RATE_HZ, SAMPLE_COUNT>(
1894 frequency_hz,
1895 duration_for_sample_count(SAMPLE_COUNT, SAMPLE_RATE_HZ),
1896 )
1897}
1898
1899#[must_use]
1903#[doc(hidden)]
1904pub const fn __tone_pcm_clip_with_duration<const SAMPLE_RATE_HZ: u32, const SAMPLE_COUNT: usize>(
1905 frequency_hz: u32,
1906 duration: core::time::Duration,
1907) -> PcmClipBuf<SAMPLE_RATE_HZ, SAMPLE_COUNT> {
1908 assert!(SAMPLE_RATE_HZ > 0, "sample_rate_hz must be > 0");
1909 let mut samples = [0_i16; SAMPLE_COUNT];
1910 let phase_step_u64 = ((frequency_hz as u64) << 32) / SAMPLE_RATE_HZ as u64;
1911 let phase_step_u32 = phase_step_u64 as u32;
1912 let mut phase_u32 = 0_u32;
1913
1914 let mut sample_index = 0usize;
1915 while sample_index < SAMPLE_COUNT {
1917 samples[sample_index] = sine_sample_from_phase(phase_u32);
1918 phase_u32 = phase_u32.wrapping_add(phase_step_u32);
1919 sample_index += 1;
1920 }
1921
1922 let max_duration = Duration::from_millis(50);
1924 assert!(max_duration.as_secs() == 0, "50ms cap must be sub-second");
1925 let attack_release_duration = match (duration.as_secs(), duration.subsec_nanos()) {
1926 (0, nanos) if nanos / 4 < max_duration.subsec_nanos() => Duration::new(0, nanos / 4),
1927 (_, _) => max_duration,
1928 };
1929 PcmClip { samples }.with_attack_release(attack_release_duration, attack_release_duration)
1930}
1931
1932#[must_use]
1937#[doc(hidden)]
1938pub const fn __pcm_clip_from_samples<const SAMPLE_RATE_HZ: u32, const SAMPLE_COUNT: usize>(
1939 samples: [i16; SAMPLE_COUNT],
1940) -> PcmClipBuf<SAMPLE_RATE_HZ, SAMPLE_COUNT> {
1941 assert!(SAMPLE_RATE_HZ > 0, "sample_rate_hz must be > 0");
1942 PcmClip { samples }
1943}
1944
1945#[must_use]
1950#[doc(hidden)]
1951pub const fn __adpcm_clip_from_parts<const SAMPLE_RATE_HZ: u32, const DATA_LEN: usize>(
1952 block_align: u16,
1953 samples_per_block: u16,
1954 pcm_sample_count: usize,
1955 data: [u8; DATA_LEN],
1956) -> AdpcmClipBuf<SAMPLE_RATE_HZ, DATA_LEN> {
1957 AdpcmClip::new(block_align, samples_per_block, pcm_sample_count, data)
1958}
1959
1960#[must_use]
1965#[doc(hidden)]
1966pub const fn __pcm_with_adpcm_block_align<
1967 const SAMPLE_RATE_HZ: u32,
1968 const SAMPLE_COUNT: usize,
1969 const DATA_LEN: usize,
1970>(
1971 source_pcm_clip: &PcmClipBuf<SAMPLE_RATE_HZ, SAMPLE_COUNT>,
1972 block_align: usize,
1973) -> AdpcmClipBuf<SAMPLE_RATE_HZ, DATA_LEN> {
1974 source_pcm_clip.with_adpcm_block_align::<DATA_LEN>(block_align)
1975}
1976
1977#[must_use]
1983#[doc(hidden)]
1984pub const fn __resample_pcm_clip<
1985 const SOURCE_HZ: u32,
1986 const SOURCE_COUNT: usize,
1987 const TARGET_HZ: u32,
1988 const TARGET_COUNT: usize,
1989>(
1990 source_pcm_clip: PcmClipBuf<SOURCE_HZ, SOURCE_COUNT>,
1991) -> PcmClipBuf<TARGET_HZ, TARGET_COUNT> {
1992 assert!(SOURCE_COUNT > 0, "source sample count must be > 0");
1993 assert!(TARGET_HZ > 0, "destination sample_rate_hz must be > 0");
1994 let expected_destination_sample_count =
1995 __resampled_sample_count(SOURCE_COUNT, SOURCE_HZ, TARGET_HZ);
1996 assert!(
1997 TARGET_COUNT == expected_destination_sample_count,
1998 "destination sample count must preserve duration"
1999 );
2000
2001 let source_samples = source_pcm_clip.samples;
2002 let mut resampled_samples = [0_i16; TARGET_COUNT];
2003 let mut sample_index = 0_usize;
2004
2005 while sample_index < TARGET_COUNT {
2007 let source_position_numerator_u128 = sample_index as u128 * SOURCE_HZ as u128;
2008 let source_index_u128 = source_position_numerator_u128 / TARGET_HZ as u128;
2009 let source_fraction_numerator_u128 = source_position_numerator_u128 % TARGET_HZ as u128;
2010 let source_index = source_index_u128 as usize;
2011
2012 resampled_samples[sample_index] = if source_index + 1 >= SOURCE_COUNT {
2013 source_samples[SOURCE_COUNT - 1]
2014 } else if source_fraction_numerator_u128 == 0 {
2015 source_samples[source_index]
2016 } else {
2017 let left_sample_i128 = source_samples[source_index] as i128;
2018 let right_sample_i128 = source_samples[source_index + 1] as i128;
2019 let sample_delta_i128 = right_sample_i128 - left_sample_i128;
2020 let denom_i128 = TARGET_HZ as i128;
2021 let numerator_i128 = sample_delta_i128 * source_fraction_numerator_u128 as i128;
2022 let rounded_i128 = if numerator_i128 >= 0 {
2023 (numerator_i128 + (denom_i128 / 2)) / denom_i128
2024 } else {
2025 (numerator_i128 - (denom_i128 / 2)) / denom_i128
2026 };
2027 clamp_i64_to_i16((left_sample_i128 + rounded_i128) as i64)
2028 };
2029
2030 sample_index += 1;
2031 }
2032
2033 PcmClip {
2034 samples: resampled_samples,
2035 }
2036}
2037
2038#[doc(hidden)]
2041pub const fn decode_adpcm_nibble_const(
2042 adpcm_nibble: u8,
2043 predictor_i32: &mut i32,
2044 step_index_i32: &mut i32,
2045) -> i16 {
2046 let step = ADPCM_STEP_TABLE[*step_index_i32 as usize];
2047 let mut delta = step >> 3;
2048
2049 if (adpcm_nibble & 0x01) != 0 {
2050 delta += step >> 2;
2051 }
2052 if (adpcm_nibble & 0x02) != 0 {
2053 delta += step >> 1;
2054 }
2055 if (adpcm_nibble & 0x04) != 0 {
2056 delta += step;
2057 }
2058
2059 if (adpcm_nibble & 0x08) != 0 {
2060 *predictor_i32 -= delta;
2061 } else {
2062 *predictor_i32 += delta;
2063 }
2064
2065 if *predictor_i32 < i16::MIN as i32 {
2066 *predictor_i32 = i16::MIN as i32;
2067 } else if *predictor_i32 > i16::MAX as i32 {
2068 *predictor_i32 = i16::MAX as i32;
2069 }
2070 *step_index_i32 += ADPCM_INDEX_TABLE[adpcm_nibble as usize];
2071 if *step_index_i32 < 0 {
2072 *step_index_i32 = 0;
2073 } else if *step_index_i32 > 88 {
2074 *step_index_i32 = 88;
2075 }
2076
2077 *predictor_i32 as i16
2078}
2079
2080const fn encode_adpcm_nibble(
2081 target_sample_i32: i32,
2082 predictor_i32: &mut i32,
2083 step_index_i32: &mut i32,
2084) -> u8 {
2085 let step = ADPCM_STEP_TABLE[*step_index_i32 as usize];
2086 let mut diff = target_sample_i32 - *predictor_i32;
2087 let mut adpcm_nibble = 0_u8;
2088 if diff < 0 {
2089 adpcm_nibble |= 0x08;
2090 diff = -diff;
2091 }
2092
2093 let mut delta = step >> 3;
2094 if diff >= step {
2095 adpcm_nibble |= 0x04;
2096 diff -= step;
2097 delta += step;
2098 }
2099 if diff >= (step >> 1) {
2100 adpcm_nibble |= 0x02;
2101 diff -= step >> 1;
2102 delta += step >> 1;
2103 }
2104 if diff >= (step >> 2) {
2105 adpcm_nibble |= 0x01;
2106 delta += step >> 2;
2107 }
2108
2109 if (adpcm_nibble & 0x08) != 0 {
2110 *predictor_i32 -= delta;
2111 } else {
2112 *predictor_i32 += delta;
2113 }
2114
2115 if *predictor_i32 < i16::MIN as i32 {
2116 *predictor_i32 = i16::MIN as i32;
2117 } else if *predictor_i32 > i16::MAX as i32 {
2118 *predictor_i32 = i16::MAX as i32;
2119 }
2120 *step_index_i32 += ADPCM_INDEX_TABLE[adpcm_nibble as usize];
2121 if *step_index_i32 < 0 {
2122 *step_index_i32 = 0;
2123 } else if *step_index_i32 > 88 {
2124 *step_index_i32 = 88;
2125 }
2126
2127 adpcm_nibble
2128}
2129
2130#[doc(hidden)]
2135#[macro_export]
2136macro_rules! pcm_clip {
2137 ($($tt:tt)*) => { $crate::__audio_clip_parse! { $($tt)* } };
2141}
2142
2143#[doc(hidden)]
2144#[macro_export]
2145macro_rules! __audio_clip_parse {
2146 (
2147 $vis:vis $name:ident {
2148 file: $file:expr,
2149 sample_rate_hz: $source_sample_rate_hz:expr,
2150 target_sample_rate_hz: $target_sample_rate_hz:expr $(,)?
2151 }
2152 ) => {
2153 $crate::__audio_clip_dispatch! {
2154 vis: $vis,
2155 name: $name,
2156 file: $file,
2157 source_sample_rate_hz: $source_sample_rate_hz,
2158 target_sample_rate_hz: $target_sample_rate_hz,
2159 }
2160 };
2161 (
2162 $vis:vis $name:ident {
2163 file: $file:expr,
2164 sample_rate_hz: $source_sample_rate_hz:expr,
2165 target_sample_rate_hz: $target_sample_rate_hz:expr,
2166 $(,)?
2167 }
2168 ) => {
2169 $crate::__audio_clip_dispatch! {
2170 vis: $vis,
2171 name: $name,
2172 file: $file,
2173 source_sample_rate_hz: $source_sample_rate_hz,
2174 target_sample_rate_hz: $target_sample_rate_hz,
2175 }
2176 };
2177 (
2178 $vis:vis $name:ident {
2179 file: $file:expr,
2180 sample_rate_hz: $sample_rate_hz:expr $(,)?
2181 }
2182 ) => {
2183 $crate::__audio_clip_dispatch! {
2184 vis: $vis,
2185 name: $name,
2186 file: $file,
2187 source_sample_rate_hz: $sample_rate_hz,
2188 target_sample_rate_hz: $sample_rate_hz,
2189 }
2190 };
2191 (
2192 $vis:vis $name:ident {
2193 file: $file:expr,
2194 sample_rate_hz: $sample_rate_hz:expr,
2195 $(,)?
2196 }
2197 ) => {
2198 $crate::__audio_clip_dispatch! {
2199 vis: $vis,
2200 name: $name,
2201 file: $file,
2202 source_sample_rate_hz: $sample_rate_hz,
2203 target_sample_rate_hz: $sample_rate_hz,
2204 }
2205 };
2206 (
2207 $vis:vis $name:ident {
2208 file: $file:expr,
2209 sample_rate_hz: $sample_rate_hz:expr,
2210 $(,)?
2211 }
2212 ) => {
2213 $crate::__audio_clip_dispatch! {
2214 vis: $vis,
2215 name: $name,
2216 file: $file,
2217 source_sample_rate_hz: $sample_rate_hz,
2218 target_sample_rate_hz: $sample_rate_hz,
2219 }
2220 };
2221 (
2223 $vis:vis $name:ident {
2224 file: $file:expr,
2225 source_sample_rate_hz: $source_sample_rate_hz:expr,
2226 target_sample_rate_hz: $target_sample_rate_hz:expr $(,)?
2227 }
2228 ) => {
2229 $crate::__audio_clip_dispatch! {
2230 vis: $vis,
2231 name: $name,
2232 file: $file,
2233 source_sample_rate_hz: $source_sample_rate_hz,
2234 target_sample_rate_hz: $target_sample_rate_hz,
2235 }
2236 };
2237 (
2238 $vis:vis $name:ident {
2239 file: $file:expr,
2240 source_sample_rate_hz: $sample_rate_hz:expr $(,)?
2241 }
2242 ) => {
2243 $crate::__audio_clip_dispatch! {
2244 vis: $vis,
2245 name: $name,
2246 file: $file,
2247 source_sample_rate_hz: $sample_rate_hz,
2248 target_sample_rate_hz: $sample_rate_hz,
2249 }
2250 };
2251}
2252
2253#[doc(hidden)]
2254#[macro_export]
2255macro_rules! __audio_clip_dispatch {
2256 (
2257 vis: $vis:vis,
2258 name: $name:ident,
2259 file: $file:expr,
2260 source_sample_rate_hz: $source_sample_rate_hz:expr,
2261 target_sample_rate_hz: $target_sample_rate_hz:expr $(,)?
2262 ) => {
2263 $crate::__audio_clip_impl! {
2264 vis: $vis,
2265 name: $name,
2266 file: $file,
2267 source_sample_rate_hz: $source_sample_rate_hz,
2268 target_sample_rate_hz: $target_sample_rate_hz,
2269 }
2270 };
2271}
2272
2273#[doc(hidden)]
2274#[macro_export]
2275macro_rules! __audio_clip_impl {
2276 (
2277 vis: $vis:vis,
2278 name: $name:ident,
2279 file: $file:expr,
2280 source_sample_rate_hz: $source_sample_rate_hz:expr,
2281 target_sample_rate_hz: $target_sample_rate_hz:expr $(,)?
2282 ) => {
2283 $crate::__paste! {
2284 const [<$name:upper _SOURCE_SAMPLE_RATE_HZ>]: u32 = $source_sample_rate_hz;
2285 const [<$name:upper _TARGET_SAMPLE_RATE_HZ>]: u32 = $target_sample_rate_hz;
2286
2287 #[allow(non_snake_case)]
2288 #[doc = concat!(
2289 "Audio clip module generated by [`pcm_clip!`](macro@crate::audio_player::pcm_clip).\n\n",
2290 "[`SAMPLE_RATE_HZ`](Self::SAMPLE_RATE_HZ), ",
2291 "[`PCM_SAMPLE_COUNT`](Self::PCM_SAMPLE_COUNT), ",
2292 "[`ADPCM_DATA_LEN`](Self::ADPCM_DATA_LEN), ",
2293 "[`pcm_clip`](Self::pcm_clip), ",
2294 "and [`adpcm_clip`](Self::adpcm_clip)."
2295 )]
2296 $vis mod $name {
2297 const SOURCE_SAMPLE_RATE_HZ: u32 = super::[<$name:upper _SOURCE_SAMPLE_RATE_HZ>];
2300 const TARGET_SAMPLE_RATE_HZ: u32 = super::[<$name:upper _TARGET_SAMPLE_RATE_HZ>];
2301 #[doc = "Sample rate in hertz for this generated clip output."]
2302 pub const SAMPLE_RATE_HZ: u32 = TARGET_SAMPLE_RATE_HZ;
2303 const AUDIO_SAMPLE_BYTES_LEN: usize = include_bytes!($file).len();
2304 const SOURCE_SAMPLE_COUNT: usize = AUDIO_SAMPLE_BYTES_LEN / 2;
2305 #[doc = "Number of samples for uncompressed (PCM) version of this clip."]
2306 pub const PCM_SAMPLE_COUNT: usize = $crate::audio_player::__resampled_sample_count(
2307 SOURCE_SAMPLE_COUNT,
2308 SOURCE_SAMPLE_RATE_HZ,
2309 TARGET_SAMPLE_RATE_HZ,
2310 );
2311 #[doc = "Byte length for compressed (ADPCM) encoding this clip."]
2312 pub const ADPCM_DATA_LEN: usize =
2313 $crate::audio_player::__adpcm_data_len_for_pcm_samples(PCM_SAMPLE_COUNT);
2314
2315 #[allow(dead_code)]
2316 type SourcePcmClip = $crate::audio_player::PcmClipBuf<
2317 { SOURCE_SAMPLE_RATE_HZ },
2318 { SOURCE_SAMPLE_COUNT },
2319 >;
2320
2321 #[doc = "`const` function that returns the uncompressed (PCM) version of this clip."]
2322 #[must_use]
2323 pub const fn pcm_clip() -> $crate::audio_player::PcmClipBuf<
2324 { SAMPLE_RATE_HZ },
2325 { PCM_SAMPLE_COUNT },
2326 > {
2327 assert!(
2328 AUDIO_SAMPLE_BYTES_LEN % 2 == 0,
2329 "audio byte length must be even for s16le"
2330 );
2331
2332 let audio_sample_s16le: &[u8; AUDIO_SAMPLE_BYTES_LEN] = include_bytes!($file);
2333 let mut samples = [0_i16; SOURCE_SAMPLE_COUNT];
2334 let mut sample_index = 0_usize;
2335 while sample_index < SOURCE_SAMPLE_COUNT {
2336 let byte_index = sample_index * 2;
2337 samples[sample_index] = i16::from_le_bytes([
2338 audio_sample_s16le[byte_index],
2339 audio_sample_s16le[byte_index + 1],
2340 ]);
2341 sample_index += 1;
2342 }
2343 $crate::audio_player::__resample_pcm_clip::<
2344 SOURCE_SAMPLE_RATE_HZ,
2345 SOURCE_SAMPLE_COUNT,
2346 TARGET_SAMPLE_RATE_HZ,
2347 PCM_SAMPLE_COUNT,
2348 >($crate::audio_player::__pcm_clip_from_samples::<
2349 SOURCE_SAMPLE_RATE_HZ,
2350 SOURCE_SAMPLE_COUNT,
2351 >(samples))
2352 }
2353
2354 #[doc = "`const` function that returns the compressed (ADPCM) encoding for this clip."]
2355 #[must_use]
2356 pub const fn adpcm_clip() -> $crate::audio_player::AdpcmClipBuf<
2357 { SAMPLE_RATE_HZ },
2358 { ADPCM_DATA_LEN },
2359 > {
2360 pcm_clip().with_adpcm::<ADPCM_DATA_LEN>()
2361 }
2362
2363 }
2364 }
2365 };
2366}
2367
2368#[doc = "Macro to \"compile in\" a compressed (ADPCM) WAV clip from an external file (includes syntax details)."]
2369#[doc = include_str!("audio_player/adpcm_clip_docs.md")]
2370#[doc = include_str!("audio_player/audio_prep_steps_1_2.md")]
2371#[doc = include_str!("audio_player/adpcm_clip_step_3.md")]
2372#[doc(inline)]
2373pub use crate::adpcm_clip;
2374
2375#[doc(hidden)]
2376#[macro_export]
2377macro_rules! adpcm_clip {
2378 ($($tt:tt)*) => { $crate::__adpcm_clip_parse! { $($tt)* } };
2379}
2380
2381#[doc(hidden)]
2382#[macro_export]
2383macro_rules! __adpcm_clip_parse {
2384 (
2385 $vis:vis $name:ident {
2386 file: $file:expr,
2387 target_sample_rate_hz: $target_sample_rate_hz:expr $(,)?
2388 }
2389 ) => {
2390 $crate::__paste! {
2391 const [<$name:upper _TARGET_SAMPLE_RATE_HZ>]: u32 = $target_sample_rate_hz;
2392
2393 #[allow(non_snake_case)]
2394 #[allow(missing_docs)]
2395 $vis mod $name {
2396 const PARSED_WAV: $crate::audio_player::ParsedAdpcmWavHeader =
2397 $crate::audio_player::__parse_adpcm_wav_header(include_bytes!($file));
2398 const SOURCE_SAMPLE_RATE_HZ: u32 = PARSED_WAV.sample_rate_hz;
2399 const TARGET_SAMPLE_RATE_HZ: u32 = super::[<$name:upper _TARGET_SAMPLE_RATE_HZ>];
2400 pub const SAMPLE_RATE_HZ: u32 = TARGET_SAMPLE_RATE_HZ;
2401
2402 const SOURCE_SAMPLE_COUNT: usize = PARSED_WAV.sample_count;
2403 #[doc = "Number of samples for uncompressed (PCM) version of this clip."]
2404 pub const PCM_SAMPLE_COUNT: usize = $crate::audio_player::__resampled_sample_count(
2405 SOURCE_SAMPLE_COUNT,
2406 SOURCE_SAMPLE_RATE_HZ,
2407 TARGET_SAMPLE_RATE_HZ,
2408 );
2409 const BLOCK_ALIGN: usize = PARSED_WAV.block_align;
2410 const SOURCE_DATA_LEN: usize = PARSED_WAV.data_chunk_len;
2411 #[doc = "Byte length for compressed (ADPCM) encoding this clip."]
2412 pub const ADPCM_DATA_LEN: usize = if TARGET_SAMPLE_RATE_HZ == SOURCE_SAMPLE_RATE_HZ {
2413 SOURCE_DATA_LEN
2414 } else {
2415 $crate::audio_player::__adpcm_data_len_for_pcm_samples_with_block_align(
2416 PCM_SAMPLE_COUNT,
2417 BLOCK_ALIGN,
2418 )
2419 };
2420 type SourceAdpcmClip = $crate::audio_player::AdpcmClipBuf<SOURCE_SAMPLE_RATE_HZ, SOURCE_DATA_LEN>;
2421
2422 #[must_use]
2423 const fn source_adpcm_clip() -> SourceAdpcmClip {
2424 let wav_bytes = include_bytes!($file);
2425 let parsed_wav = $crate::audio_player::__parse_adpcm_wav_header(wav_bytes);
2426 assert!(parsed_wav.block_align <= u16::MAX as usize, "block_align too large");
2427 assert!(
2428 parsed_wav.samples_per_block <= u16::MAX as usize,
2429 "samples_per_block too large"
2430 );
2431
2432 let mut adpcm_data = [0_u8; SOURCE_DATA_LEN];
2433 let mut data_index = 0usize;
2434 while data_index < SOURCE_DATA_LEN {
2435 adpcm_data[data_index] = wav_bytes[parsed_wav.data_chunk_start + data_index];
2436 data_index += 1;
2437 }
2438
2439 $crate::audio_player::__adpcm_clip_from_parts(
2440 parsed_wav.block_align as u16,
2441 parsed_wav.samples_per_block as u16,
2442 parsed_wav.sample_count,
2443 adpcm_data,
2444 )
2445 }
2446
2447 #[doc = "`const` function that returns the uncompressed (PCM) version of this clip."]
2448 #[must_use]
2449 pub const fn pcm_clip() -> $crate::audio_player::PcmClipBuf<SAMPLE_RATE_HZ, PCM_SAMPLE_COUNT> {
2450 $crate::audio_player::__resample_pcm_clip::<
2451 SOURCE_SAMPLE_RATE_HZ,
2452 SOURCE_SAMPLE_COUNT,
2453 TARGET_SAMPLE_RATE_HZ,
2454 PCM_SAMPLE_COUNT,
2455 >(source_adpcm_clip().with_pcm::<SOURCE_SAMPLE_COUNT>())
2456 }
2457
2458 #[doc = "`const` function that returns the compressed (ADPCM) encoding for this clip."]
2459 #[must_use]
2460 pub const fn adpcm_clip() -> $crate::audio_player::AdpcmClipBuf<SAMPLE_RATE_HZ, ADPCM_DATA_LEN> {
2461 if TARGET_SAMPLE_RATE_HZ == SOURCE_SAMPLE_RATE_HZ {
2462 let wav_bytes = include_bytes!($file);
2463 let parsed_wav = $crate::audio_player::__parse_adpcm_wav_header(wav_bytes);
2464 assert!(parsed_wav.block_align <= u16::MAX as usize, "block_align too large");
2465 assert!(
2466 parsed_wav.samples_per_block <= u16::MAX as usize,
2467 "samples_per_block too large"
2468 );
2469 let mut adpcm_data = [0_u8; ADPCM_DATA_LEN];
2470 let mut data_index = 0usize;
2471 while data_index < ADPCM_DATA_LEN {
2472 adpcm_data[data_index] =
2473 wav_bytes[parsed_wav.data_chunk_start + data_index];
2474 data_index += 1;
2475 }
2476 $crate::audio_player::__adpcm_clip_from_parts(
2477 parsed_wav.block_align as u16,
2478 parsed_wav.samples_per_block as u16,
2479 parsed_wav.sample_count,
2480 adpcm_data,
2481 )
2482 } else {
2483 $crate::audio_player::__pcm_with_adpcm_block_align::<
2484 SAMPLE_RATE_HZ,
2485 PCM_SAMPLE_COUNT,
2486 ADPCM_DATA_LEN,
2487 >(&pcm_clip(), BLOCK_ALIGN)
2488 }
2489 }
2490
2491 }
2492 }
2493 };
2494
2495 (
2496 $vis:vis $name:ident {
2497 file: $file:expr $(,)?
2498 }
2499 ) => {
2500 $crate::__adpcm_clip_parse! {
2501 $vis $name {
2502 file: $file,
2503 target_sample_rate_hz: $crate::audio_player::__parse_adpcm_wav_header(include_bytes!($file)).sample_rate_hz,
2504 }
2505 }
2506 };
2507}
2508
2509#[doc(hidden)]
2521#[macro_export]
2522macro_rules! tone {
2523 ($frequency_hz:expr, $sample_rate_hz:expr, $duration:expr) => {
2524 $crate::audio_player::__tone_pcm_clip_with_duration::<
2525 { $sample_rate_hz },
2526 { $crate::audio_player::__samples_for_duration($duration, $sample_rate_hz) },
2527 >($frequency_hz, $duration)
2528 };
2529}
2530
2531#[doc = "Macro to \"compile in\" an uncompressed (PCM) clip from an external file (includes syntax details)."]
2532#[doc = include_str!("audio_player/pcm_clip_docs.md")]
2533#[doc = include_str!("audio_player/audio_prep_steps_1_2.md")]
2534#[doc = include_str!("audio_player/pcm_clip_step_3.md")]
2535#[doc(inline)]
2536pub use crate::pcm_clip;
2537#[doc(inline)]
2538pub use crate::tone;