|
| 1 | +use crate::conversions::sample_rate::rubato::{ResampleInner, RubatoAsyncResample}; |
| 2 | +use crate::conversions::Interpolation; |
| 3 | +use crate::math::gcd; |
| 4 | +use crate::source::ResampleConfig; |
| 5 | +use crate::{ConstSource, Sample, SampleRate, Source}; |
| 6 | + |
| 7 | +use crate::const_source::IntoDynamicSource; |
| 8 | +use crate::conversions::sample_rate::{InSamples, OutSamples}; |
| 9 | + |
| 10 | +/// Resamples an audio source to a target sample rate using Rubato. |
| 11 | +pub struct SampleRateConvertor< |
| 12 | + const SR_IN: u32, |
| 13 | + const SR_OUT: u32, |
| 14 | + const CH: u16, |
| 15 | + S: ConstSource<SR_IN, CH>, |
| 16 | +> { |
| 17 | + // Option so we can take out the source and rebuild the resampler without unsafe |
| 18 | + inner: Option<ResampleInner<IntoDynamicSource<SR_IN, CH, S>>>, |
| 19 | +} |
| 20 | + |
| 21 | +#[derive(thiserror::Error)] |
| 22 | +#[error("The resampler was already running")] |
| 23 | +pub struct ResamplerRunning< |
| 24 | + const SR_IN: u32, |
| 25 | + const SR_OUT: u32, |
| 26 | + const CH: u16, |
| 27 | + S: ConstSource<SR_IN, CH>, |
| 28 | +>(SampleRateConvertor<SR_IN, SR_OUT, CH, S>); |
| 29 | + |
| 30 | +impl<const SR_IN: u32, const SR_OUT: u32, const CH: u16, S: ConstSource<SR_IN, CH>> |
| 31 | + SampleRateConvertor<SR_IN, SR_OUT, CH, S> |
| 32 | +{ |
| 33 | + pub(crate) fn new(source: S) -> Self { |
| 34 | + let target_rate = |
| 35 | + const { SampleRate::new(SR_OUT).expect("SampleRate (SR_OUT) may not be zero") }; |
| 36 | + Self { |
| 37 | + inner: Some(Self::create_resampler( |
| 38 | + source.into_dynamic_source(), |
| 39 | + target_rate, |
| 40 | + ResampleConfig::default(), |
| 41 | + )), |
| 42 | + } |
| 43 | + } |
| 44 | + |
| 45 | + /// Further configure the resampler created with [`with_sample_rate`](ConstSource::with_sample_rate). |
| 46 | + /// You usually do not need to do this unless you have a specific use-case that requires very |
| 47 | + /// fast or extremely high quality resampling. The [`ResampleConfig`] has a number of factories |
| 48 | + /// with good defaults for such use-cases. |
| 49 | + /// |
| 50 | + /// # Errors |
| 51 | + /// If the resampler can no longer be reconfigured, usually after yielding |
| 52 | + /// the first sample. |
| 53 | + /// |
| 54 | + /// # Example |
| 55 | + /// ``` |
| 56 | + /// # use rodio::generators::const_source::Silence; |
| 57 | + /// # use rodio::SampleRate; |
| 58 | + /// # fn hi() -> Option<()> { // to enable ? in the example |
| 59 | + /// use rodio::ConstSource; |
| 60 | + /// use rodio::conversions::ResampleConfig; |
| 61 | + /// |
| 62 | + /// let source: Silence<44100> = Silence::new(); |
| 63 | + /// let resampled = source |
| 64 | + /// .with_sample_rate::<48000>() |
| 65 | + /// .with_config(ResampleConfig::fast()); |
| 66 | + /// # Some(()) |
| 67 | + /// # } |
| 68 | + /// # hi().unwrap(); |
| 69 | + /// ``` |
| 70 | + #[allow(clippy::result_large_err, reason = "the Ok variant is the same size")] |
| 71 | + pub fn with_config( |
| 72 | + mut self, |
| 73 | + config: ResampleConfig, |
| 74 | + ) -> Result<Self, ResamplerRunning<SR_IN, SR_OUT, CH, S>> { |
| 75 | + if !self.resampler().can_reconfigure() { |
| 76 | + return Err(ResamplerRunning(self)); |
| 77 | + } |
| 78 | + |
| 79 | + let source = self |
| 80 | + .inner |
| 81 | + .take() |
| 82 | + .expect( |
| 83 | + "we are the only ones who set this to none and we set it to \ |
| 84 | + some at the end of this fn", |
| 85 | + ) |
| 86 | + .into_inner(); |
| 87 | + let target_rate = SampleRate::new(SR_OUT).expect("already checked in 'new'"); |
| 88 | + |
| 89 | + Ok(Self { |
| 90 | + inner: Some(Self::create_resampler(source, target_rate, config)), |
| 91 | + }) |
| 92 | + } |
| 93 | + |
| 94 | + fn resampler(&self) -> &ResampleInner<IntoDynamicSource<SR_IN, CH, S>> { |
| 95 | + self.inner |
| 96 | + .as_ref() |
| 97 | + .expect("never none outside `with_config`") |
| 98 | + } |
| 99 | + |
| 100 | + fn resampler_mut(&mut self) -> &mut ResampleInner<IntoDynamicSource<SR_IN, CH, S>> { |
| 101 | + self.inner |
| 102 | + .as_mut() |
| 103 | + .expect("never none outside `with_config`") |
| 104 | + } |
| 105 | + |
| 106 | + fn create_resampler( |
| 107 | + source: IntoDynamicSource<SR_IN, CH, S>, |
| 108 | + target_rate: SampleRate, |
| 109 | + config: ResampleConfig, |
| 110 | + ) -> ResampleInner<IntoDynamicSource<SR_IN, CH, S>> { |
| 111 | + if source.sample_rate() == target_rate { |
| 112 | + let channels = source.channels(); |
| 113 | + ResampleInner::Passthrough { |
| 114 | + source_rate: source.sample_rate(), |
| 115 | + source, |
| 116 | + input_span_pos: InSamples::ZERO, |
| 117 | + channels, |
| 118 | + } |
| 119 | + } else { |
| 120 | + match config { |
| 121 | + ResampleConfig::Poly { degree, chunk_size } => { |
| 122 | + let resampler = |
| 123 | + RubatoAsyncResample::new_poly(source, target_rate, chunk_size, degree) |
| 124 | + .expect("Failed to create polynomial resampler"); |
| 125 | + ResampleInner::Poly(resampler) |
| 126 | + } |
| 127 | + ResampleConfig::Sinc(mut sinc) => { |
| 128 | + #[cfg(feature = "rubato-fft")] |
| 129 | + if sinc.is_supported_fixed_ratio(target_rate, source_rate) { |
| 130 | + let resampler = RubatoFftResample::new( |
| 131 | + source, |
| 132 | + target_rate, |
| 133 | + sinc.chunk_size, |
| 134 | + sinc.sub_chunks, |
| 135 | + ) |
| 136 | + .expect("Failed to create FFT resampler"); |
| 137 | + return ResampleInner::Fft(resampler); |
| 138 | + } |
| 139 | + |
| 140 | + if sinc.is_supported_fixed_ratio(target_rate, source.sample_rate()) { |
| 141 | + sinc.interpolation = Interpolation::Nearest; |
| 142 | + let g = gcd(target_rate.get(), source.sample_rate().get()); |
| 143 | + let numer = target_rate.get() / g; |
| 144 | + let denom = source.sample_rate().get() / g; |
| 145 | + let ratio = numer.max(denom) as usize; |
| 146 | + sinc.oversampling_factor = ratio; |
| 147 | + } |
| 148 | + ResampleInner::Sinc(sinc.build(source, target_rate)) |
| 149 | + } |
| 150 | + } |
| 151 | + } |
| 152 | + } |
| 153 | +} |
| 154 | + |
| 155 | +impl<const SR_IN: u32, const SR_OUT: u32, const CH: u16, S: ConstSource<SR_IN, CH>> |
| 156 | + ConstSource<SR_OUT, CH> for SampleRateConvertor<SR_IN, SR_OUT, CH, S> |
| 157 | +{ |
| 158 | + fn total_duration(&self) -> Option<std::time::Duration> { |
| 159 | + self.resampler().inner().total_duration() |
| 160 | + } |
| 161 | +} |
| 162 | + |
| 163 | +impl<const SR_IN: u32, const SR_OUT: u32, const CH: u16, S> Iterator |
| 164 | + for SampleRateConvertor<SR_IN, SR_OUT, CH, S> |
| 165 | +where |
| 166 | + S: ConstSource<SR_IN, CH>, |
| 167 | +{ |
| 168 | + type Item = Sample; |
| 169 | + |
| 170 | + #[inline] |
| 171 | + fn next(&mut self) -> Option<Self::Item> { |
| 172 | + match self.resampler_mut() { |
| 173 | + ResampleInner::Passthrough { source, .. } => source.next(), |
| 174 | + ResampleInner::Poly(resampler) => resampler.next_sample(), |
| 175 | + ResampleInner::Sinc(resampler) => resampler.next_sample(), |
| 176 | + #[cfg(feature = "rubato-fft")] |
| 177 | + ResampleInner::Fft(resampler) => resampler.next_sample(), |
| 178 | + } |
| 179 | + } |
| 180 | + |
| 181 | + #[inline] |
| 182 | + fn size_hint(&self) -> (usize, Option<usize>) { |
| 183 | + match self.resampler() { |
| 184 | + ResampleInner::Passthrough { source, .. } => source.size_hint(), |
| 185 | + ResampleInner::Poly(resampler) | ResampleInner::Sinc(resampler) => { |
| 186 | + let adjusted_for_resampling = |samples| { |
| 187 | + InSamples(samples).resampled_by(resampler.resample_ratio) |
| 188 | + + resampler.output.len() |
| 189 | + + resampler |
| 190 | + .frames_being_resampled |
| 191 | + .samples(resampler.output.channels) |
| 192 | + }; |
| 193 | + let (lower, upper) = resampler.input.size_hint(); |
| 194 | + let lower = adjusted_for_resampling(lower); |
| 195 | + let upper = upper.map(adjusted_for_resampling); |
| 196 | + (lower.raw(), upper.as_ref().map(OutSamples::raw)) |
| 197 | + } |
| 198 | + #[cfg(feature = "rubato-fft")] |
| 199 | + ResampleInner::Fft(resampler) => { |
| 200 | + let adjusted_for_resampling = |samples| { |
| 201 | + InSamples(samples).resampled_by(resampler.resample_ratio) |
| 202 | + + resampler.output.len() |
| 203 | + + resampler |
| 204 | + .frames_being_resampled |
| 205 | + .samples(resampler.output.channels) |
| 206 | + }; |
| 207 | + let (lower, upper) = resampler.input.size_hint(); |
| 208 | + let lower = adjusted_for_resampling(lower); |
| 209 | + let upper = upper.map(adjusted_for_resampling); |
| 210 | + (lower.raw(), upper.as_ref().map(OutSamples::raw)) |
| 211 | + } |
| 212 | + } |
| 213 | + } |
| 214 | +} |
0 commit comments