|
1 | | -/// Streaming 8-bit PCM (Tier B). |
| 1 | +use crate::fixed::lerp_i8; |
| 2 | + |
| 3 | +/// Streaming 8-bit PCM (Tier B) with fractional speed resampling. |
2 | 4 | #[derive(Debug, Clone, Copy)] |
3 | 5 | pub struct Pcm8Stream<'a> { |
4 | 6 | data: &'a [u8], |
5 | | - pos: usize, |
| 7 | + phase_q16: u32, |
| 8 | + speed_q16: u32, |
6 | 9 | looped: bool, |
| 10 | + is_signed: bool, |
7 | 11 | } |
8 | 12 |
|
9 | 13 | impl<'a> Pcm8Stream<'a> { |
10 | 14 | pub fn new(data: &'a [u8], flags: u8) -> Self { |
| 15 | + Self::with_speed(data, flags, 65536) |
| 16 | + } |
| 17 | + |
| 18 | + pub fn with_speed(data: &'a [u8], flags: u8, speed_q16: u32) -> Self { |
11 | 19 | Self { |
12 | 20 | data, |
13 | | - pos: 0, |
| 21 | + phase_q16: 0, |
| 22 | + speed_q16, |
14 | 23 | looped: flags & crate::tier::flags::LOOP != 0, |
| 24 | + is_signed: flags & crate::tier::flags::SIGNED != 0, |
15 | 25 | } |
16 | 26 | } |
17 | 27 |
|
| 28 | + pub fn set_speed_q16(&mut self, speed_q16: u32) { |
| 29 | + self.speed_q16 = speed_q16; |
| 30 | + } |
| 31 | + |
| 32 | + pub fn speed_q16(&self) -> u32 { |
| 33 | + self.speed_q16 |
| 34 | + } |
| 35 | + |
18 | 36 | pub fn reset(&mut self) { |
19 | | - self.pos = 0; |
| 37 | + self.phase_q16 = 0; |
20 | 38 | } |
21 | 39 |
|
22 | 40 | pub fn is_done(&self) -> bool { |
23 | | - !self.looped && self.pos >= self.data.len() |
| 41 | + if self.data.is_empty() { |
| 42 | + return true; |
| 43 | + } |
| 44 | + let pos = (self.phase_q16 >> 16) as usize; |
| 45 | + !self.looped && pos >= self.data.len() |
| 46 | + } |
| 47 | + |
| 48 | + #[inline] |
| 49 | + fn get_sample(&self, idx: usize) -> i8 { |
| 50 | + if idx >= self.data.len() { |
| 51 | + 0 |
| 52 | + } else if self.is_signed { |
| 53 | + self.data[idx] as i8 |
| 54 | + } else { |
| 55 | + self.data[idx].wrapping_sub(128) as i8 |
| 56 | + } |
24 | 57 | } |
25 | | -} |
26 | 58 |
|
27 | | -impl<'a> Pcm8Stream<'a> { |
28 | 59 | pub fn next_sample(&mut self) -> Option<i8> { |
29 | | - if self.pos >= self.data.len() { |
30 | | - if self.looped && !self.data.is_empty() { |
31 | | - self.pos = 0; |
| 60 | + if self.data.is_empty() { |
| 61 | + return None; |
| 62 | + } |
| 63 | + |
| 64 | + let curr_idx = (self.phase_q16 >> 16) as usize; |
| 65 | + |
| 66 | + if curr_idx >= self.data.len() { |
| 67 | + if self.looped { |
| 68 | + let len = self.data.len(); |
| 69 | + let wrap = (curr_idx % len) << 16; |
| 70 | + self.phase_q16 = (wrap as u32) | (self.phase_q16 & 0xFFFF); |
32 | 71 | } else { |
33 | 72 | return None; |
34 | 73 | } |
35 | 74 | } |
36 | | - let s = self.data[self.pos] as i8; |
37 | | - self.pos += 1; |
38 | | - Some(s) |
| 75 | + |
| 76 | + let idx = (self.phase_q16 >> 16) as usize; |
| 77 | + let frac = ((self.phase_q16 >> 8) & 0xFF) as u8; |
| 78 | + |
| 79 | + let a = self.get_sample(idx); |
| 80 | + let next_idx = if idx + 1 < self.data.len() { |
| 81 | + idx + 1 |
| 82 | + } else if self.looped { |
| 83 | + 0 |
| 84 | + } else { |
| 85 | + idx |
| 86 | + }; |
| 87 | + let b = self.get_sample(next_idx); |
| 88 | + |
| 89 | + let sample = lerp_i8(a, b, frac); |
| 90 | + self.phase_q16 = self.phase_q16.wrapping_add(self.speed_q16); |
| 91 | + |
| 92 | + Some(sample) |
39 | 93 | } |
40 | 94 | } |
0 commit comments