fixed_resample/
resampler.rs

1use std::{num::NonZeroUsize, ops::Range};
2
3use arrayvec::ArrayVec;
4use rubato::Sample;
5
6use crate::resampler_type::ResamplerType;
7
8/// The quality of the resampling algorithm used for a [`FixedResampler`].
9#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
10pub enum ResampleQuality {
11    /// Decent quality, fast performance, low latency
12    ///
13    /// This is recommended for most realtime applications where low latency
14    /// is desired.
15    ///
16    /// Internally this uses the [`FastFixedIn`] resampler from rubato with
17    /// linear interpolation.
18    Low,
19    #[default]
20    /// Great quality, medium performance, high latency
21    ///
22    /// This is recommended for most non-realtime applications where higher
23    /// latency is not an issue.
24    ///
25    /// Note, this resampler type adds a significant amount of latency (in
26    /// the hundreds of frames), so prefer to use the "Low" option if low
27    /// latency is desired.
28    ///
29    /// If the `fft-resampler` feature is not enabled, then this will fall
30    /// back to "Low".
31    ///
32    /// Internally this uses the [`FftFixedIn`] resampler from rubato with
33    /// a chunk size of `1024` and `2` sub-chunks.
34    High,
35}
36
37/// Options for processes the last packet in a resampler.
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub struct LastPacketInfo {
40    /// The desired number of output frames that should be sent via the
41    /// `on_output_packet` closure.
42    ///
43    /// If this is `None`, then the last packet sent may contain extra
44    /// padded zeros on the end.
45    pub desired_output_frames: Option<u64>,
46}
47
48/// An easy-to-use resampler with a fixed ratio.
49///
50/// Internally this uses the `rubato` crate.
51pub struct FixedResampler<T: Sample, const MAX_CHANNELS: usize> {
52    resampler: ResamplerType<T>,
53    tmp_deintlv_in_buf: Vec<T>,
54    tmp_deintlv_out_buf: Vec<T>,
55    tmp_intlv_buf: Vec<T>,
56    tmp_deintlv_in_buf_len: usize,
57    num_channels: NonZeroUsize,
58    input_block_frames: usize,
59    max_output_block_frames: usize,
60    output_delay: usize,
61    delay_frames_left: usize,
62    in_sample_rate: u32,
63    out_sample_rate: u32,
64    ratio: f64,
65    interleaved: bool,
66}
67
68impl<T: Sample, const MAX_CHANNELS: usize> FixedResampler<T, MAX_CHANNELS> {
69    /// Create a new [`FixedResampler`].
70    ///
71    /// * `num_channels` - The number of audio channels.
72    /// * `in_sample_rate` - The sample rate of the input data.
73    /// * `out_sample_rate` - The sample rate of the output data.
74    /// * `quality` - The quality of the resampling algorithm to use.
75    /// * `interleaved` - If you plan on using [`FixedResampler::process_interleaved`],
76    /// then set this to `true`. Otherwise, you can set this to `false` to
77    /// save a bit of memory.
78    ///
79    /// # Panics
80    /// Panics if:
81    /// * `num_channels == 0`
82    /// * `num_channels > MAX_CHANNELS`
83    /// * `in_sample_rate == 0`
84    /// * `out_sample_rate == 0`
85    pub fn new(
86        num_channels: NonZeroUsize,
87        in_sample_rate: u32,
88        out_sample_rate: u32,
89        quality: ResampleQuality,
90        interleaved: bool,
91    ) -> Self {
92        Self::from_custom(
93            ResamplerType::from_quality(
94                in_sample_rate,
95                out_sample_rate,
96                num_channels,
97                quality.into(),
98            ),
99            in_sample_rate,
100            out_sample_rate,
101            interleaved,
102        )
103    }
104
105    /// Create a new resampler that uses the `SincFixedIn` resampler from rubato.
106    ///
107    /// This has similar quality to the [`rubato::FftFixedIn`] resampler used
108    /// for [`ResampleQuality::High`], but with much lower performance. Use
109    /// this if you need a non-integer ratio (i.e. repitching a sample).
110    ///
111    /// * `in_sample_rate` - The sample rate of the input data.
112    /// * `ratio` - The resampling ratio (`output / input`)
113    /// * `num_channels` - The number of channels
114    ///
115    /// More specifically, this creates a resampler with the following parameters:
116    /// ```rust,ignore
117    /// SincInterpolationParameters {
118    ///     sinc_len: 128,
119    ///     f_cutoff: rubato::calculate_cutoff(128, WindowFunction::Blackman2),
120    ///     interpolation: SincInterpolationType::Cubic,
121    ///     oversampling_factor: 512,
122    ///     window: WindowFunction::Blackman2,
123    /// }
124    /// ```
125    ///
126    /// # Panics
127    /// Panics if:
128    /// * `in_sample_rate == 0`
129    /// * `ratio <= 0.0`
130    /// * `num_channels > MAX_CHANNELS`
131    pub fn arbitrary_ratio_sinc(
132        in_sample_rate: u32,
133        ratio: f64,
134        num_channels: NonZeroUsize,
135        interleaved: bool,
136    ) -> Self {
137        Self::from_custom_inner(
138            ResamplerType::arbitrary_ratio_sinc(ratio, num_channels),
139            in_sample_rate,
140            (in_sample_rate as f64 * ratio).ceil() as u32,
141            interleaved,
142            ratio,
143        )
144    }
145
146    /// Create a new [`FixedResampler`] using the custom resampler.
147    ///
148    /// * `resampler` - The resampler to use.
149    /// * `in_sample_rate` - The sample rate of the input data.
150    /// * `out_sample_rate` - The sample rate of the output data.
151    /// * `interleaved` - If you plan on using [`FixedResampler::process_interleaved`],
152    /// then set this to `true`. Otherwise, you can set this to `false` to
153    /// save a bit of memory.
154    ///
155    /// # Panics
156    /// Panics if:
157    /// * `resampler.num_channels() == 0`
158    /// * `resampler.num_channels() == 0 > MAX_CHANNELS`
159    /// * `in_sample_rate == 0`
160    /// * `out_sample_rate == 0`
161    pub fn from_custom(
162        resampler: impl Into<ResamplerType<T>>,
163        in_sample_rate: u32,
164        out_sample_rate: u32,
165        interleaved: bool,
166    ) -> Self {
167        Self::from_custom_inner(
168            resampler,
169            in_sample_rate,
170            out_sample_rate,
171            interleaved,
172            out_sample_rate as f64 / in_sample_rate as f64,
173        )
174    }
175
176    fn from_custom_inner(
177        resampler: impl Into<ResamplerType<T>>,
178        in_sample_rate: u32,
179        out_sample_rate: u32,
180        interleaved: bool,
181        ratio: f64,
182    ) -> Self {
183        assert_ne!(in_sample_rate, 0);
184        assert_ne!(out_sample_rate, 0);
185
186        let mut resampler: ResamplerType<T> = resampler.into();
187
188        let num_channels = NonZeroUsize::new(resampler.num_channels()).unwrap();
189
190        assert!(num_channels.get() <= MAX_CHANNELS);
191
192        let input_block_frames = resampler.input_frames_max();
193        let max_output_block_frames = resampler.output_frames_max();
194        let output_delay = resampler.output_delay();
195
196        let tmp_in_buf_len = input_block_frames * num_channels.get();
197        let tmp_out_buf_len = max_output_block_frames * num_channels.get();
198
199        let mut tmp_deintlv_in_buf = Vec::new();
200        tmp_deintlv_in_buf.reserve_exact(tmp_in_buf_len);
201        tmp_deintlv_in_buf.resize(tmp_in_buf_len, T::zero());
202
203        let mut tmp_deintlv_out_buf = Vec::new();
204        tmp_deintlv_out_buf.reserve_exact(tmp_out_buf_len);
205        tmp_deintlv_out_buf.resize(tmp_out_buf_len, T::zero());
206
207        let tmp_intlv_buf = if interleaved && num_channels.get() > 1 {
208            let intlv_buf_len =
209                input_block_frames.max(max_output_block_frames) * num_channels.get();
210            let mut v = Vec::new();
211            v.reserve_exact(intlv_buf_len);
212            v.resize(intlv_buf_len, T::zero());
213            v
214        } else {
215            Vec::new()
216        };
217
218        Self {
219            resampler,
220            tmp_deintlv_in_buf,
221            tmp_deintlv_out_buf,
222            tmp_intlv_buf,
223            tmp_deintlv_in_buf_len: 0,
224            num_channels,
225            input_block_frames,
226            max_output_block_frames,
227            output_delay,
228            delay_frames_left: output_delay,
229            in_sample_rate,
230            out_sample_rate,
231            ratio,
232            interleaved,
233        }
234    }
235
236    /// The number of channels configured for this resampler.
237    pub fn num_channels(&self) -> NonZeroUsize {
238        self.num_channels
239    }
240
241    /// The input sample rate configured for this resampler.
242    pub fn in_sample_rate(&self) -> u32 {
243        self.in_sample_rate
244    }
245
246    /// The output sample rate configured for this resampler.
247    pub fn out_sample_rate(&self) -> u32 {
248        self.out_sample_rate
249    }
250
251    /// The resampling ratio `output / input`.
252    pub fn ratio(&self) -> f64 {
253        self.ratio
254    }
255
256    /// The number of frames (samples in a single channel of audio) that appear in
257    /// a single packet of input data in the internal resampler.
258    pub fn input_block_frames(&self) -> usize {
259        self.input_block_frames
260    }
261
262    /// The maximum number of frames (samples in a single channel of audio) that can
263    /// appear in a single call to the `on_output_packet` closure in
264    /// [`FixedResampler::process`] and [`FixedResampler::process_interleaved`].
265    pub fn max_output_block_frames(&self) -> usize {
266        self.max_output_block_frames
267    }
268
269    /// The delay introduced by the internal resampler in number of output frames (
270    /// samples in a single channel of audio).
271    pub fn output_delay(&self) -> usize {
272        self.output_delay
273    }
274
275    /// Whether or not the `interleaved` argument was set to `true` in the constructor.
276    pub fn is_interleaved(&self) -> bool {
277        self.interleaved
278    }
279
280    /// The number of frames (samples in a single channel of audio) that are needed
281    /// for an output buffer given the number of input frames.
282    pub fn out_alloc_frames(&self, input_frames: u64) -> u64 {
283        ((input_frames * self.out_sample_rate as u64) / self.in_sample_rate as u64) + 1
284    }
285
286    #[allow(unused)]
287    pub(crate) fn tmp_input_frames(&self) -> usize {
288        self.tmp_deintlv_in_buf_len
289    }
290
291    /// Process the given de-interleaved input data and return packets of de-interleaved
292    /// resampled output data.
293    ///
294    /// * `input` - The de-interleaved channels of input data.
295    /// * `input_range` - The range in each input channel to read from.
296    /// * `on_output_packet` - Gets called whenever there is a new packet of resampled
297    /// output data. The output data is in de-interleaved format.
298    /// * `last_packet` - If this is `Some`, then any leftover input samples in the
299    /// buffer will be flushed out and the resampler reset. Use this if this is the
300    /// last/only packet of input data when used in a non-realtime context.
301    /// * `trim_delay` - If `true`, then the initial padded zeros introduced by the
302    /// internal resampler will be trimmed off. If this is being used in a realtime
303    /// context, then prefer to set this to `false`.
304    ///
305    /// This method is realtime-safe.
306    ///
307    /// # Panics
308    /// Panics if:
309    /// * `input.len() < self.num_channels()`
310    /// * The `input_range` is out of bounds for any of the input channels.
311    pub fn process<Vin: AsRef<[T]>>(
312        &mut self,
313        input: &[Vin],
314        input_range: Range<usize>,
315        mut on_output_packet: impl FnMut(ArrayVec<&[T], MAX_CHANNELS>),
316        last_packet: Option<LastPacketInfo>,
317        trim_delay: bool,
318    ) {
319        assert!(input.len() >= self.num_channels.get());
320
321        {
322            let mut on_output_packet_inner =
323                move |output_packet: ArrayVec<&[T], MAX_CHANNELS>, _tmp_intlv_buf: &mut Vec<T>| {
324                    (on_output_packet)(output_packet);
325                };
326
327            let total_input_frames = input_range.end - input_range.start;
328
329            let mut tmp_deintlv_in_buf_slices: ArrayVec<&mut [T], MAX_CHANNELS> = self
330                .tmp_deintlv_in_buf
331                .chunks_exact_mut(self.input_block_frames)
332                .collect();
333            let mut tmp_deintlv_out_buf_slices: ArrayVec<&mut [T], MAX_CHANNELS> = self
334                .tmp_deintlv_out_buf
335                .chunks_exact_mut(self.max_output_block_frames)
336                .collect();
337
338            let mut input_frames_processed = 0;
339            let mut output_frames_processed = 0;
340
341            let desired_output_frames = last_packet.and_then(|info| info.desired_output_frames);
342
343            while input_frames_processed < total_input_frames {
344                if self.tmp_deintlv_in_buf_len == 0
345                    && (total_input_frames - input_frames_processed) >= self.input_block_frames
346                {
347                    // We can use the input data directly to avoid an extra copy.
348
349                    let input_slices: ArrayVec<&[T], MAX_CHANNELS> = input
350                        [..self.num_channels.get()]
351                        .iter()
352                        .map(|s| {
353                            &s.as_ref()[input_range.start + input_frames_processed
354                                ..input_range.start
355                                    + input_frames_processed
356                                    + self.input_block_frames]
357                        })
358                        .collect();
359
360                    resample_inner(
361                        &mut self.resampler,
362                        &input_slices,
363                        &mut tmp_deintlv_out_buf_slices,
364                        &mut on_output_packet_inner,
365                        &mut output_frames_processed,
366                        desired_output_frames,
367                        &mut self.delay_frames_left,
368                        trim_delay,
369                        &mut self.tmp_intlv_buf,
370                    );
371
372                    input_frames_processed += self.input_block_frames;
373                } else {
374                    let copy_frames = (self.input_block_frames - self.tmp_deintlv_in_buf_len)
375                        .min(total_input_frames - input_frames_processed);
376
377                    for (in_slice_ch, input_ch) in
378                        tmp_deintlv_in_buf_slices.iter_mut().zip(input.iter())
379                    {
380                        in_slice_ch[self.tmp_deintlv_in_buf_len
381                            ..self.tmp_deintlv_in_buf_len + copy_frames]
382                            .copy_from_slice(
383                                &input_ch.as_ref()[input_range.start + input_frames_processed
384                                    ..input_range.start + input_frames_processed + copy_frames],
385                            );
386                    }
387
388                    self.tmp_deintlv_in_buf_len += copy_frames;
389                    input_frames_processed += copy_frames;
390
391                    if self.tmp_deintlv_in_buf_len < self.input_block_frames {
392                        // Must wait for more data before resampling the next packet.
393                        break;
394                    }
395
396                    resample_inner(
397                        &mut self.resampler,
398                        &tmp_deintlv_in_buf_slices,
399                        &mut tmp_deintlv_out_buf_slices,
400                        &mut on_output_packet_inner,
401                        &mut output_frames_processed,
402                        desired_output_frames,
403                        &mut self.delay_frames_left,
404                        trim_delay,
405                        &mut self.tmp_intlv_buf,
406                    );
407
408                    self.tmp_deintlv_in_buf_len = 0;
409                }
410            }
411
412            if last_packet.is_some() {
413                process_last_packet(
414                    &mut tmp_deintlv_in_buf_slices,
415                    &mut tmp_deintlv_out_buf_slices,
416                    &mut self.resampler,
417                    &mut on_output_packet_inner,
418                    &mut output_frames_processed,
419                    desired_output_frames,
420                    &mut self.delay_frames_left,
421                    trim_delay,
422                    &mut self.tmp_intlv_buf,
423                    self.tmp_deintlv_in_buf_len,
424                );
425            }
426        }
427
428        if last_packet.is_some() {
429            self.reset();
430        }
431    }
432
433    /// Process the given interleaved input data and return packets of interleaved
434    /// resampled output data.
435    ///
436    /// * `input` - The interleaved input data.
437    /// * `on_output_packet` - Gets called whenever there is a new packet of resampled
438    /// output data. The output data is in interleaved format.
439    /// * `last_packet` - If this is `Some`, then any leftover input samples in the
440    /// buffer will be flushed out and the resampler reset. Use this if this is the
441    /// last/only packet of input data when used in a non-realtime context.
442    /// * `trim_delay` - If `true`, then the initial padded zeros introduced by the
443    /// internal resampler will be trimmed off. If this is being used in a realtime
444    /// context, then prefer to set this to `false`.
445    ///
446    /// This method is realtime-safe.
447    ///
448    /// # Panics
449    /// Panics if the `interleaved` argument in the constructor was `false`.
450    pub fn process_interleaved(
451        &mut self,
452        input: &[T],
453        mut on_output_packet: impl FnMut(&[T]),
454        last_packet: Option<LastPacketInfo>,
455        trim_delay: bool,
456    ) {
457        assert!(self.interleaved, "The constructor argument \"interleaved\" must be set to \"true\" in order to call FixedResampler::process_interleaved");
458
459        {
460            let num_channels = self.num_channels;
461
462            let mut on_output_packet_inner =
463                move |output_packet: ArrayVec<&[T], MAX_CHANNELS>, tmp_intlv_buf: &mut Vec<T>| {
464                    let frames = output_packet[0].len();
465
466                    if num_channels.get() == 1 {
467                        (on_output_packet)(&output_packet[0]);
468                    } else {
469                        fast_interleave::interleave_variable(
470                            &output_packet,
471                            0..frames,
472                            tmp_intlv_buf.as_mut_slice(),
473                            num_channels,
474                        );
475
476                        (on_output_packet)(&tmp_intlv_buf[..frames * num_channels.get()]);
477                    }
478                };
479
480            let total_input_frames = input.len() / self.num_channels;
481
482            let mut tmp_deintlv_in_buf_slices: ArrayVec<&mut [T], MAX_CHANNELS> = self
483                .tmp_deintlv_in_buf
484                .chunks_exact_mut(self.input_block_frames)
485                .collect();
486            let mut tmp_deintlv_out_buf_slices: ArrayVec<&mut [T], MAX_CHANNELS> = self
487                .tmp_deintlv_out_buf
488                .chunks_exact_mut(self.max_output_block_frames)
489                .collect();
490
491            let mut input_frames_processed = 0;
492            let mut output_frames_processed = 0;
493
494            let desired_output_frames = last_packet.and_then(|info| info.desired_output_frames);
495
496            while input_frames_processed < total_input_frames {
497                let copy_frames = (self.input_block_frames - self.tmp_deintlv_in_buf_len)
498                    .min(total_input_frames - input_frames_processed);
499
500                fast_interleave::deinterleave_variable(
501                    &input[input_frames_processed * self.num_channels.get()
502                        ..(input_frames_processed + copy_frames) * self.num_channels.get()],
503                    self.num_channels,
504                    &mut tmp_deintlv_in_buf_slices,
505                    self.tmp_deintlv_in_buf_len..self.tmp_deintlv_in_buf_len + copy_frames,
506                );
507
508                self.tmp_deintlv_in_buf_len += copy_frames;
509                input_frames_processed += copy_frames;
510
511                if self.tmp_deintlv_in_buf_len < self.input_block_frames {
512                    // Must wait for more data before resampling the next packet.
513                    break;
514                }
515
516                resample_inner(
517                    &mut self.resampler,
518                    &tmp_deintlv_in_buf_slices,
519                    &mut tmp_deintlv_out_buf_slices,
520                    &mut on_output_packet_inner,
521                    &mut output_frames_processed,
522                    desired_output_frames,
523                    &mut self.delay_frames_left,
524                    trim_delay,
525                    &mut self.tmp_intlv_buf,
526                );
527
528                self.tmp_deintlv_in_buf_len = 0;
529            }
530
531            if last_packet.is_some() {
532                process_last_packet(
533                    &mut tmp_deintlv_in_buf_slices,
534                    &mut tmp_deintlv_out_buf_slices,
535                    &mut self.resampler,
536                    &mut on_output_packet_inner,
537                    &mut output_frames_processed,
538                    desired_output_frames,
539                    &mut self.delay_frames_left,
540                    trim_delay,
541                    &mut self.tmp_intlv_buf,
542                    self.tmp_deintlv_in_buf_len,
543                );
544            }
545        }
546
547        if last_packet.is_some() {
548            self.reset();
549        }
550    }
551
552    /// Reset the state of the resampler.
553    ///
554    /// This method is realtime-safe.
555    pub fn reset(&mut self) {
556        self.resampler.reset();
557        self.tmp_deintlv_in_buf_len = 0;
558        self.delay_frames_left = self.output_delay;
559    }
560}
561
562impl<T: Sample, const MAX_CHANNELS: usize> Into<ResamplerType<T>>
563    for FixedResampler<T, MAX_CHANNELS>
564{
565    fn into(self) -> ResamplerType<T> {
566        self.resampler
567    }
568}
569
570fn process_last_packet<T: Sample, const MAX_CHANNELS: usize>(
571    tmp_deintlv_in_buf_slices: &mut ArrayVec<&mut [T], MAX_CHANNELS>,
572    tmp_deintlv_out_buf_slices: &mut ArrayVec<&mut [T], MAX_CHANNELS>,
573    resampler: &mut ResamplerType<T>,
574    on_output_packet: &mut impl FnMut(ArrayVec<&[T], MAX_CHANNELS>, &mut Vec<T>),
575    output_frames_processed: &mut u64,
576    desired_output_frames: Option<u64>,
577    delay_frames_left: &mut usize,
578    trim_delay: bool,
579    tmp_intlv_buf: &mut Vec<T>,
580    tmp_deintlv_in_buf_len: usize,
581) {
582    if tmp_deintlv_in_buf_len > 0 {
583        for ch in tmp_deintlv_in_buf_slices.iter_mut() {
584            ch[tmp_deintlv_in_buf_len..].fill(T::zero());
585        }
586
587        resample_inner(
588            resampler,
589            &tmp_deintlv_in_buf_slices,
590            tmp_deintlv_out_buf_slices,
591            on_output_packet,
592            output_frames_processed,
593            desired_output_frames,
594            delay_frames_left,
595            trim_delay,
596            tmp_intlv_buf,
597        );
598    }
599
600    for ch in tmp_deintlv_in_buf_slices.iter_mut() {
601        ch.fill(T::zero());
602    }
603
604    if let Some(desired_output_frames) = desired_output_frames {
605        if *output_frames_processed >= desired_output_frames {
606            return;
607        }
608
609        while *output_frames_processed < desired_output_frames {
610            resample_inner(
611                resampler,
612                &tmp_deintlv_in_buf_slices,
613                tmp_deintlv_out_buf_slices,
614                on_output_packet,
615                output_frames_processed,
616                Some(desired_output_frames),
617                delay_frames_left,
618                trim_delay,
619                tmp_intlv_buf,
620            );
621        }
622    } else {
623        resample_inner(
624            resampler,
625            &tmp_deintlv_in_buf_slices,
626            tmp_deintlv_out_buf_slices,
627            on_output_packet,
628            output_frames_processed,
629            desired_output_frames,
630            delay_frames_left,
631            trim_delay,
632            tmp_intlv_buf,
633        );
634    }
635}
636
637fn resample_inner<T: Sample, Vin: AsRef<[T]>, const MAX_CHANNELS: usize>(
638    resampler: &mut ResamplerType<T>,
639    input: &[Vin],
640    tmp_deintlv_out_buf_slices: &mut ArrayVec<&mut [T], MAX_CHANNELS>,
641    on_output_packet: &mut impl FnMut(ArrayVec<&[T], MAX_CHANNELS>, &mut Vec<T>),
642    output_frames_processed: &mut u64,
643    desired_output_frames: Option<u64>,
644    delay_frames_left: &mut usize,
645    trim_delay: bool,
646    tmp_intlv_buf: &mut Vec<T>,
647) {
648    let (_, output_frames) = resampler
649        .process_into_buffer(input, tmp_deintlv_out_buf_slices, None)
650        .unwrap();
651
652    let (output_packet_start, mut packet_output_frames) = if trim_delay && *delay_frames_left > 0 {
653        let delay_frames = output_frames.min(*delay_frames_left);
654        *delay_frames_left -= delay_frames;
655        (delay_frames, output_frames - delay_frames)
656    } else {
657        (0, output_frames)
658    };
659
660    if let Some(desired_output_frames) = desired_output_frames {
661        if desired_output_frames <= *output_frames_processed {
662            packet_output_frames = 0;
663        } else if (desired_output_frames - *output_frames_processed) < packet_output_frames as u64 {
664            packet_output_frames = (desired_output_frames - *output_frames_processed) as usize
665        }
666    }
667
668    if packet_output_frames > 0 {
669        let out_packet_slices: ArrayVec<&[T], MAX_CHANNELS> = tmp_deintlv_out_buf_slices
670            .iter()
671            .map(|s| &s[output_packet_start..output_packet_start + packet_output_frames])
672            .collect();
673
674        (on_output_packet)(out_packet_slices, tmp_intlv_buf);
675    }
676
677    *output_frames_processed += packet_output_frames as u64;
678}