LCOV - code coverage report
Current view: top level - media/libcubeb/src - cubeb_resampler_internal.h (source / functions) Hit Total Coverage
Test: output.info Lines: 0 165 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 68 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright © 2016 Mozilla Foundation
       3             :  *
       4             :  * This program is made available under an ISC-style license.  See the
       5             :  * accompanying file LICENSE for details.
       6             :  */
       7             : 
       8             : #if !defined(CUBEB_RESAMPLER_INTERNAL)
       9             : #define CUBEB_RESAMPLER_INTERNAL
      10             : 
      11             : #include <cmath>
      12             : #include <cassert>
      13             : #include <algorithm>
      14             : #include <memory>
      15             : #ifdef CUBEB_GECKO_BUILD
      16             : #include "mozilla/UniquePtr.h"
      17             : // In libc++, symbols such as std::unique_ptr may be defined in std::__1.
      18             : // The _LIBCPP_BEGIN_NAMESPACE_STD and _LIBCPP_END_NAMESPACE_STD macros
      19             : // will expand to the correct namespace.
      20             : #ifdef _LIBCPP_BEGIN_NAMESPACE_STD
      21             : #define MOZ_BEGIN_STD_NAMESPACE _LIBCPP_BEGIN_NAMESPACE_STD
      22             : #define MOZ_END_STD_NAMESPACE _LIBCPP_END_NAMESPACE_STD
      23             : #else
      24             : #define MOZ_BEGIN_STD_NAMESPACE namespace std {
      25             : #define MOZ_END_STD_NAMESPACE }
      26             : #endif
      27             : MOZ_BEGIN_STD_NAMESPACE
      28             :   using mozilla::DefaultDelete;
      29             :   using mozilla::UniquePtr;
      30             :   #define default_delete DefaultDelete
      31             :   #define unique_ptr UniquePtr
      32             : MOZ_END_STD_NAMESPACE
      33             : #endif
      34             : #include "cubeb/cubeb.h"
      35             : #include "cubeb_utils.h"
      36             : #include "cubeb-speex-resampler.h"
      37             : #include "cubeb_resampler.h"
      38             : #include <stdio.h>
      39             : 
      40             : /* This header file contains the internal C++ API of the resamplers, for testing. */
      41             : 
      42             : int to_speex_quality(cubeb_resampler_quality q);
      43             : 
      44           0 : struct cubeb_resampler {
      45             :   virtual long fill(void * input_buffer, long * input_frames_count,
      46             :                     void * output_buffer, long frames_needed) = 0;
      47             :   virtual long latency() = 0;
      48           0 :   virtual ~cubeb_resampler() {}
      49             : };
      50             : 
      51             : /** Base class for processors. This is just used to share methods for now. */
      52             : class processor {
      53             : public:
      54           0 :   explicit processor(uint32_t channels)
      55           0 :     : channels(channels)
      56           0 :   {}
      57             : protected:
      58           0 :   size_t frames_to_samples(size_t frames)
      59             :   {
      60           0 :     return frames * channels;
      61             :   }
      62           0 :   size_t samples_to_frames(size_t samples)
      63             :   {
      64           0 :     assert(!(samples % channels));
      65           0 :     return samples / channels;
      66             :   }
      67             :   /** The number of channel of the audio buffers to be resampled. */
      68             :   const uint32_t channels;
      69             : };
      70             : 
      71             : template<typename T>
      72           0 : class passthrough_resampler : public cubeb_resampler
      73             :                             , public processor {
      74             : public:
      75             :   passthrough_resampler(cubeb_stream * s,
      76             :                         cubeb_data_callback cb,
      77             :                         void * ptr,
      78             :                         uint32_t input_channels);
      79             : 
      80             :   virtual long fill(void * input_buffer, long * input_frames_count,
      81             :                     void * output_buffer, long output_frames);
      82             : 
      83           0 :   virtual long latency()
      84             :   {
      85           0 :     return 0;
      86             :   }
      87             : 
      88             : private:
      89             :   cubeb_stream * const stream;
      90             :   const cubeb_data_callback data_callback;
      91             :   void * const user_ptr;
      92             :   /* This allows to buffer some input to account for the fact that we buffer
      93             :    * some inputs. */
      94             :   auto_array<T> internal_input_buffer;
      95             : };
      96             : 
      97             : /** Bidirectional resampler, can resample an input and an output stream, or just
      98             :  * an input stream or output stream. In this case a delay is inserted in the
      99             :  * opposite direction to keep the streams synchronized. */
     100             : template<typename T, typename InputProcessing, typename OutputProcessing>
     101             : class cubeb_resampler_speex : public cubeb_resampler {
     102             : public:
     103             :   cubeb_resampler_speex(InputProcessing * input_processor,
     104             :                         OutputProcessing * output_processor,
     105             :                         cubeb_stream * s,
     106             :                         cubeb_data_callback cb,
     107             :                         void * ptr);
     108             : 
     109             :   virtual ~cubeb_resampler_speex();
     110             : 
     111             :   virtual long fill(void * input_buffer, long * input_frames_count,
     112             :                     void * output_buffer, long output_frames_needed);
     113             : 
     114           0 :   virtual long latency()
     115             :   {
     116           0 :     if (input_processor && output_processor) {
     117           0 :       assert(input_processor->latency() == output_processor->latency());
     118           0 :       return input_processor->latency();
     119           0 :     } else if (input_processor) {
     120           0 :       return input_processor->latency();
     121             :     } else {
     122           0 :       return output_processor->latency();
     123             :     }
     124             :   }
     125             : 
     126             : private:
     127             :   typedef long(cubeb_resampler_speex::*processing_callback)(T * input_buffer, long * input_frames_count, T * output_buffer, long output_frames_needed);
     128             : 
     129             :   long fill_internal_duplex(T * input_buffer, long * input_frames_count,
     130             :                             T * output_buffer, long output_frames_needed);
     131             :   long fill_internal_input(T * input_buffer, long * input_frames_count,
     132             :                            T * output_buffer, long output_frames_needed);
     133             :   long fill_internal_output(T * input_buffer, long * input_frames_count,
     134             :                             T * output_buffer, long output_frames_needed);
     135             : 
     136             :   std::unique_ptr<InputProcessing> input_processor;
     137             :   std::unique_ptr<OutputProcessing> output_processor;
     138             :   processing_callback fill_internal;
     139             :   cubeb_stream * const stream;
     140             :   const cubeb_data_callback data_callback;
     141             :   void * const user_ptr;
     142             : };
     143             : 
     144             : /** Handles one way of a (possibly) duplex resampler, working on interleaved
     145             :  * audio buffers of type T. This class is designed so that the number of frames
     146             :  * coming out of the resampler can be precisely controled. It manages its own
     147             :  * input buffer, and can use the caller's output buffer, or allocate its own. */
     148             : template<typename T>
     149             : class cubeb_resampler_speex_one_way : public processor {
     150             : public:
     151             :   /** The sample type of this resampler, either 16-bit integers or 32-bit
     152             :    * floats. */
     153             :   typedef T sample_type;
     154             :   /** Construct a resampler resampling from #source_rate to #target_rate, that
     155             :    * can be arbitrary, strictly positive number.
     156             :    * @parameter channels The number of channels this resampler will resample.
     157             :    * @parameter source_rate The sample-rate of the audio input.
     158             :    * @parameter target_rate The sample-rate of the audio output.
     159             :    * @parameter quality A number between 0 (fast, low quality) and 10 (slow,
     160             :    * high quality). */
     161           0 :   cubeb_resampler_speex_one_way(uint32_t channels,
     162             :                                 uint32_t source_rate,
     163             :                                 uint32_t target_rate,
     164             :                                 int quality)
     165             :   : processor(channels)
     166           0 :   , resampling_ratio(static_cast<float>(source_rate) / target_rate)
     167             :   , additional_latency(0)
     168           0 :   , leftover_samples(0)
     169             :   {
     170             :     int r;
     171           0 :     speex_resampler = speex_resampler_init(channels, source_rate,
     172             :                                            target_rate, quality, &r);
     173           0 :     assert(r == RESAMPLER_ERR_SUCCESS && "resampler allocation failure");
     174           0 :   }
     175             : 
     176             :   /** Destructor, deallocate the resampler */
     177           0 :   virtual ~cubeb_resampler_speex_one_way()
     178             :   {
     179           0 :     speex_resampler_destroy(speex_resampler);
     180           0 :   }
     181             : 
     182             :   /** Sometimes, it is necessary to add latency on one way of a two-way
     183             :    * resampler so that the stream are synchronized. This must be called only on
     184             :    * a fresh resampler, otherwise, silent samples will be inserted in the
     185             :    * stream.
     186             :    * @param frames the number of frames of latency to add. */
     187           0 :   void add_latency(size_t frames)
     188             :   {
     189           0 :     additional_latency += frames;
     190           0 :     resampling_in_buffer.push_silence(frames_to_samples(frames));
     191           0 :   }
     192             : 
     193             :   /* Fill the resampler with `input_frame_count` frames. */
     194           0 :   void input(T * input_buffer, size_t input_frame_count)
     195             :   {
     196           0 :     resampling_in_buffer.push(input_buffer,
     197             :                               frames_to_samples(input_frame_count));
     198           0 :   }
     199             : 
     200             :   /** Outputs exactly `output_frame_count` into `output_buffer`.
     201             :     * `output_buffer` has to be at least `output_frame_count` long. */
     202           0 :   size_t output(T * output_buffer, size_t output_frame_count)
     203             :   {
     204           0 :     uint32_t in_len = samples_to_frames(resampling_in_buffer.length());
     205           0 :     uint32_t out_len = output_frame_count;
     206             : 
     207           0 :     speex_resample(resampling_in_buffer.data(), &in_len,
     208             :                    output_buffer, &out_len);
     209             : 
     210             :     /* This shifts back any unresampled samples to the beginning of the input
     211             :        buffer. */
     212           0 :     resampling_in_buffer.pop(nullptr, frames_to_samples(in_len));
     213             : 
     214           0 :     return out_len;
     215             :   }
     216             : 
     217           0 :   size_t output_for_input(uint32_t input_frames)
     218             :   {
     219           0 :     return (size_t)floorf((input_frames + samples_to_frames(resampling_in_buffer.length()))
     220           0 :                          / resampling_ratio);
     221             :   }
     222             : 
     223             :   /** Returns a buffer containing exactly `output_frame_count` resampled frames.
     224             :     * The consumer should not hold onto the pointer. */
     225           0 :   T * output(size_t output_frame_count, size_t * input_frames_used)
     226             :   {
     227           0 :     if (resampling_out_buffer.capacity() < frames_to_samples(output_frame_count)) {
     228           0 :       resampling_out_buffer.reserve(frames_to_samples(output_frame_count));
     229             :     }
     230             : 
     231           0 :     uint32_t in_len = samples_to_frames(resampling_in_buffer.length());
     232           0 :     uint32_t out_len = output_frame_count;
     233             : 
     234           0 :     speex_resample(resampling_in_buffer.data(), &in_len,
     235             :                    resampling_out_buffer.data(), &out_len);
     236             : 
     237           0 :     assert(out_len == output_frame_count);
     238             : 
     239             :     /* This shifts back any unresampled samples to the beginning of the input
     240             :        buffer. */
     241           0 :     resampling_in_buffer.pop(nullptr, frames_to_samples(in_len));
     242           0 :     *input_frames_used = in_len;
     243             : 
     244           0 :     return resampling_out_buffer.data();
     245             :   }
     246             : 
     247             :   /** Get the latency of the resampler, in output frames. */
     248           0 :   uint32_t latency() const
     249             :   {
     250             :     /* The documentation of the resampler talks about "samples" here, but it
     251             :      * only consider a single channel here so it's the same number of frames. */
     252           0 :     int latency = 0;
     253             : 
     254           0 :     latency =
     255           0 :       speex_resampler_get_output_latency(speex_resampler) + additional_latency;
     256             : 
     257           0 :     assert(latency >= 0);
     258             : 
     259           0 :     return latency;
     260             :   }
     261             : 
     262             :   /** Returns the number of frames to pass in the input of the resampler to have
     263             :    * exactly `output_frame_count` resampled frames. This can return a number
     264             :    * slightly bigger than what is strictly necessary, but it guaranteed that the
     265             :    * number of output frames will be exactly equal. */
     266           0 :   uint32_t input_needed_for_output(uint32_t output_frame_count)
     267             :   {
     268           0 :     int32_t unresampled_frames_left = samples_to_frames(resampling_in_buffer.length());
     269           0 :     int32_t resampled_frames_left = samples_to_frames(resampling_out_buffer.length());
     270             :     float input_frames_needed =
     271           0 :       (output_frame_count - unresampled_frames_left) * resampling_ratio
     272           0 :         - resampled_frames_left;
     273           0 :     if (input_frames_needed < 0) {
     274           0 :       return 0;
     275             :     }
     276           0 :     return (uint32_t)ceilf(input_frames_needed);
     277             :   }
     278             : 
     279             :   /** Returns a pointer to the input buffer, that contains empty space for at
     280             :    * least `frame_count` elements. This is useful so that consumer can directly
     281             :    * write into the input buffer of the resampler. The pointer returned is
     282             :    * adjusted so that leftover data are not overwritten.
     283             :    */
     284           0 :   T * input_buffer(size_t frame_count)
     285             :   {
     286           0 :     leftover_samples = resampling_in_buffer.length();
     287           0 :     resampling_in_buffer.reserve(leftover_samples +
     288             :                                  frames_to_samples(frame_count));
     289           0 :     return resampling_in_buffer.data() + leftover_samples;
     290             :   }
     291             : 
     292             :   /** This method works with `input_buffer`, and allows to inform the processor
     293             :       how much frames have been written in the provided buffer. */
     294           0 :   void written(size_t written_frames)
     295             :   {
     296           0 :     resampling_in_buffer.set_length(leftover_samples +
     297             :                                     frames_to_samples(written_frames));
     298           0 :   }
     299             : private:
     300             :   /** Wrapper for the speex resampling functions to have a typed
     301             :     * interface. */
     302           0 :   void speex_resample(float * input_buffer, uint32_t * input_frame_count,
     303             :                       float * output_buffer, uint32_t * output_frame_count)
     304             :   {
     305             : #ifndef NDEBUG
     306             :     int rv;
     307           0 :     rv =
     308             : #endif
     309           0 :       speex_resampler_process_interleaved_float(speex_resampler,
     310             :                                                 input_buffer,
     311             :                                                 input_frame_count,
     312             :                                                 output_buffer,
     313             :                                                 output_frame_count);
     314           0 :     assert(rv == RESAMPLER_ERR_SUCCESS);
     315           0 :   }
     316             : 
     317           0 :   void speex_resample(short * input_buffer, uint32_t * input_frame_count,
     318             :                       short * output_buffer, uint32_t * output_frame_count)
     319             :   {
     320             : #ifndef NDEBUG
     321             :     int rv;
     322           0 :     rv =
     323             : #endif
     324           0 :       speex_resampler_process_interleaved_int(speex_resampler,
     325             :                                               input_buffer,
     326             :                                               input_frame_count,
     327             :                                               output_buffer,
     328             :                                               output_frame_count);
     329           0 :     assert(rv == RESAMPLER_ERR_SUCCESS);
     330           0 :   }
     331             :   /** The state for the speex resampler used internaly. */
     332             :   SpeexResamplerState * speex_resampler;
     333             :   /** Source rate / target rate. */
     334             :   const float resampling_ratio;
     335             :   /** Storage for the input frames, to be resampled. Also contains
     336             :    * any unresampled frames after resampling. */
     337             :   auto_array<T> resampling_in_buffer;
     338             :   /* Storage for the resampled frames, to be passed back to the caller. */
     339             :   auto_array<T> resampling_out_buffer;
     340             :   /** Additional latency inserted into the pipeline for synchronisation. */
     341             :   uint32_t additional_latency;
     342             :   /** When `input_buffer` is called, this allows tracking the number of samples
     343             :       that were in the buffer. */
     344             :   uint32_t leftover_samples;
     345             : };
     346             : 
     347             : /** This class allows delaying an audio stream by `frames` frames. */
     348             : template<typename T>
     349           0 : class delay_line : public processor {
     350             : public:
     351             :   /** Constructor
     352             :    * @parameter frames the number of frames of delay.
     353             :    * @parameter channels the number of channels of this delay line. */
     354           0 :   delay_line(uint32_t frames, uint32_t channels)
     355             :     : processor(channels)
     356             :     , length(frames)
     357           0 :     , leftover_samples(0)
     358             :   {
     359             :     /* Fill the delay line with some silent frames to add latency. */
     360           0 :     delay_input_buffer.push_silence(frames * channels);
     361           0 :   }
     362             :   /* Add some latency to the delay line.
     363             :    * @param frames the number of frames of latency to add. */
     364           0 :   void add_latency(size_t frames)
     365             :   {
     366           0 :     length += frames;
     367           0 :     delay_input_buffer.push_silence(frames_to_samples(frames));
     368           0 :   }
     369             :   /** Push some frames into the delay line.
     370             :    * @parameter buffer the frames to push.
     371             :    * @parameter frame_count the number of frames in #buffer. */
     372           0 :   void input(T * buffer, uint32_t frame_count)
     373             :   {
     374           0 :     delay_input_buffer.push(buffer, frames_to_samples(frame_count));
     375           0 :   }
     376             :   /** Pop some frames from the internal buffer, into a internal output buffer.
     377             :    * @parameter frames_needed the number of frames to be returned.
     378             :    * @return a buffer containing the delayed frames. The consumer should not
     379             :    * hold onto the pointer. */
     380           0 :   T * output(uint32_t frames_needed, size_t * input_frames_used)
     381             :   {
     382           0 :     if (delay_output_buffer.capacity() < frames_to_samples(frames_needed)) {
     383           0 :       delay_output_buffer.reserve(frames_to_samples(frames_needed));
     384             :     }
     385             : 
     386           0 :     delay_output_buffer.clear();
     387           0 :     delay_output_buffer.push(delay_input_buffer.data(),
     388             :                              frames_to_samples(frames_needed));
     389           0 :     delay_input_buffer.pop(nullptr, frames_to_samples(frames_needed));
     390           0 :     *input_frames_used = frames_needed;
     391             : 
     392           0 :     return delay_output_buffer.data();
     393             :   }
     394             :   /** Get a pointer to the first writable location in the input buffer>
     395             :    * @parameter frames_needed the number of frames the user needs to write into
     396             :    * the buffer.
     397             :    * @returns a pointer to a location in the input buffer where #frames_needed
     398             :    * can be writen. */
     399           0 :   T * input_buffer(uint32_t frames_needed)
     400             :   {
     401           0 :     leftover_samples = delay_input_buffer.length();
     402           0 :     delay_input_buffer.reserve(leftover_samples + frames_to_samples(frames_needed));
     403           0 :     return delay_input_buffer.data() + leftover_samples;
     404             :   }
     405             :   /** This method works with `input_buffer`, and allows to inform the processor
     406             :       how much frames have been written in the provided buffer. */
     407           0 :   void written(size_t frames_written)
     408             :   {
     409           0 :     delay_input_buffer.set_length(leftover_samples +
     410             :                                   frames_to_samples(frames_written));
     411           0 :   }
     412             :   /** Drains the delay line, emptying the buffer.
     413             :    * @parameter output_buffer the buffer in which the frames are written.
     414             :    * @parameter frames_needed the maximum number of frames to write.
     415             :    * @return the actual number of frames written. */
     416           0 :   size_t output(T * output_buffer, uint32_t frames_needed)
     417             :   {
     418           0 :     uint32_t in_len = samples_to_frames(delay_input_buffer.length());
     419           0 :     uint32_t out_len = frames_needed;
     420             : 
     421           0 :     uint32_t to_pop = std::min(in_len, out_len);
     422             : 
     423           0 :     delay_input_buffer.pop(output_buffer, frames_to_samples(to_pop));
     424             : 
     425           0 :     return to_pop;
     426             :   }
     427             :   /** Returns the number of frames one needs to input into the delay line to get
     428             :    * #frames_needed frames back.
     429             :    * @parameter frames_needed the number of frames one want to write into the
     430             :    * delay_line
     431             :    * @returns the number of frames one will get. */
     432           0 :   size_t input_needed_for_output(uint32_t frames_needed)
     433             :   {
     434           0 :     return frames_needed;
     435             :   }
     436             :   /** Returns the number of frames produces for `input_frames` frames in input */
     437           0 :   size_t output_for_input(uint32_t input_frames)
     438             :   {
     439           0 :     return input_frames;
     440             :   }
     441             :   /** The number of frames this delay line delays the stream by.
     442             :    * @returns The number of frames of delay. */
     443           0 :   size_t latency()
     444             :   {
     445           0 :     return length;
     446             :   }
     447             : private:
     448             :   /** The length, in frames, of this delay line */
     449             :   uint32_t length;
     450             :   /** When `input_buffer` is called, this allows tracking the number of samples
     451             :       that where in the buffer. */
     452             :   uint32_t leftover_samples;
     453             :   /** The input buffer, where the delay is applied. */
     454             :   auto_array<T> delay_input_buffer;
     455             :   /** The output buffer. This is only ever used if using the ::output with a
     456             :    * single argument. */
     457             :   auto_array<T> delay_output_buffer;
     458             : };
     459             : 
     460             : /** This sits behind the C API and is more typed. */
     461             : template<typename T>
     462             : cubeb_resampler *
     463           0 : cubeb_resampler_create_internal(cubeb_stream * stream,
     464             :                                 cubeb_stream_params * input_params,
     465             :                                 cubeb_stream_params * output_params,
     466             :                                 unsigned int target_rate,
     467             :                                 cubeb_data_callback callback,
     468             :                                 void * user_ptr,
     469             :                                 cubeb_resampler_quality quality)
     470             : {
     471           0 :   std::unique_ptr<cubeb_resampler_speex_one_way<T>> input_resampler = nullptr;
     472           0 :   std::unique_ptr<cubeb_resampler_speex_one_way<T>> output_resampler = nullptr;
     473           0 :   std::unique_ptr<delay_line<T>> input_delay = nullptr;
     474           0 :   std::unique_ptr<delay_line<T>> output_delay = nullptr;
     475             : 
     476           0 :   assert((input_params || output_params) &&
     477             :          "need at least one valid parameter pointer.");
     478             : 
     479             :   /* All the streams we have have a sample rate that matches the target
     480             :      sample rate, use a no-op resampler, that simply forwards the buffers to the
     481             :      callback. */
     482           0 :   if (((input_params && input_params->rate == target_rate) &&
     483           0 :       (output_params && output_params->rate == target_rate)) ||
     484           0 :       (input_params && !output_params && (input_params->rate == target_rate)) ||
     485           0 :       (output_params && !input_params && (output_params->rate == target_rate))) {
     486           0 :     return new passthrough_resampler<T>(stream, callback,
     487             :                                         user_ptr,
     488           0 :                                         input_params ? input_params->channels : 0);
     489             :   }
     490             : 
     491             :   /* Determine if we need to resampler one or both directions, and create the
     492             :      resamplers. */
     493           0 :   if (output_params && (output_params->rate != target_rate)) {
     494           0 :     output_resampler.reset(
     495           0 :         new cubeb_resampler_speex_one_way<T>(output_params->channels,
     496             :                                              target_rate,
     497             :                                              output_params->rate,
     498           0 :                                              to_speex_quality(quality)));
     499           0 :     if (!output_resampler) {
     500           0 :       return NULL;
     501             :     }
     502             :   }
     503             : 
     504           0 :   if (input_params && (input_params->rate != target_rate)) {
     505           0 :     input_resampler.reset(
     506           0 :         new cubeb_resampler_speex_one_way<T>(input_params->channels,
     507             :                                              input_params->rate,
     508             :                                              target_rate,
     509           0 :                                              to_speex_quality(quality)));
     510           0 :     if (!input_resampler) {
     511           0 :       return NULL;
     512             :     }
     513             :   }
     514             : 
     515             :   /* If we resample only one direction but we have a duplex stream, insert a
     516             :    * delay line with a length equal to the resampler latency of the
     517             :    * other direction so that the streams are synchronized. */
     518           0 :   if (input_resampler && !output_resampler && input_params && output_params) {
     519           0 :     output_delay.reset(new delay_line<T>(input_resampler->latency(),
     520             :                                          output_params->channels));
     521           0 :     if (!output_delay) {
     522           0 :       return NULL;
     523             :     }
     524           0 :   } else if (output_resampler && !input_resampler && input_params && output_params) {
     525           0 :     input_delay.reset(new delay_line<T>(output_resampler->latency(),
     526             :                                         input_params->channels));
     527           0 :     if (!input_delay) {
     528           0 :       return NULL;
     529             :     }
     530             :   }
     531             : 
     532           0 :   if (input_resampler && output_resampler) {
     533           0 :     return new cubeb_resampler_speex<T,
     534             :                                      cubeb_resampler_speex_one_way<T>,
     535             :                                      cubeb_resampler_speex_one_way<T>>
     536             :                                        (input_resampler.release(),
     537             :                                         output_resampler.release(),
     538           0 :                                         stream, callback, user_ptr);
     539           0 :   } else if (input_resampler) {
     540           0 :     return new cubeb_resampler_speex<T,
     541             :                                      cubeb_resampler_speex_one_way<T>,
     542             :                                      delay_line<T>>
     543             :                                       (input_resampler.release(),
     544             :                                        output_delay.release(),
     545           0 :                                        stream, callback, user_ptr);
     546             :   } else {
     547           0 :     return new cubeb_resampler_speex<T,
     548             :                                      delay_line<T>,
     549             :                                      cubeb_resampler_speex_one_way<T>>
     550             :                                       (input_delay.release(),
     551             :                                        output_resampler.release(),
     552           0 :                                        stream, callback, user_ptr);
     553             :   }
     554             : }
     555             : 
     556             : #endif /* CUBEB_RESAMPLER_INTERNAL */

Generated by: LCOV version 1.13