示例#1
0
文件: Resampler.cpp 项目: taqu/opus
    s32 Resampler::read(LSfloat* pcm, u32 numSamples, Stream* stream)
    {
        if(dstSamplesPerSec_ == srcSamplesPerSec_){
            if(NULL != convertTypeFunc_){
                s32 readSamples = stream->read_float(sharedBuffer0_, numSamples);

                if(readSamples<0){
                    return readSamples;
                }
                convertTypeFunc_(pcm, sharedBuffer0_, readSamples);
                return readSamples;

            } else{
                return stream->read_float(pcm, numSamples);
            }

        }else{
            s32 readSamples = stream->read_float(sharedBuffer0_, numSamples);
            if(readSamples<0){
                return readSamples;
            }

            u32 inN = readSamples;
            u32 outN = static_cast<u32>(resampleRate_*readSamples);
            if(NULL != convertTypeFunc_){
                speex_resampler_process_interleaved_float(resampler_, sharedBuffer0_, &inN, sharedBuffer1_, &outN);
                convertTypeFunc_(pcm, sharedBuffer1_, outN);
            }else{
                speex_resampler_process_interleaved_float(resampler_, sharedBuffer0_, &inN, pcm, &outN);
            }
            return static_cast<s32>(outN);
        }
    }
示例#2
0
//存储回音数据
void AudioInput::addEcho(const void *data, unsigned int nsamp) {
	while (nsamp > 0) {
		unsigned int left = min(nsamp, iEchoLength - iEchoFilled);

		if (bEchoMulti) {
			const unsigned int samples = left * iEchoChannels;
			if (eEchoFormat == SampleFloat)
				for (unsigned int i=0;i<samples;++i)
					pfEchoInput[i] = reinterpret_cast<const float *>(data)[i];
			else
				for (unsigned int i=0;i<samples;++i)
					pfEchoInput[i] = static_cast<float>(reinterpret_cast<const short *>(data)[i]) * (1.0f / 32768.f);
		} else {
			imfEcho(pfEchoInput + iEchoFilled, data, left, iEchoChannels);
		}

		iEchoFilled += left;
		nsamp -= left;

		if (nsamp > 0) {
			if (eEchoFormat == SampleFloat)
				data = reinterpret_cast<const float *>(data) + left * iEchoChannels;
			else
				data = reinterpret_cast<const short *>(data) + left * iEchoChannels;
		}

		if (iEchoFilled == iEchoLength) {
			iEchoFilled = 0;

			float *ptr = srsEcho ? pfOutput : pfEchoInput;
			if (srsEcho) {
				spx_uint32_t inlen = iEchoLength;
				spx_uint32_t outlen = iFrameSize;
				speex_resampler_process_interleaved_float(srsEcho, pfEchoInput, &inlen, pfOutput, &outlen);
			}

			short *outbuff = new short[iEchoFrameSize];

			const float mul = 32768.f;
			for (unsigned int j=0;j<iEchoFrameSize;++j)
				outbuff[j] = static_cast<short>(ptr[j] * mul);

			iJitterSeq = min(iJitterSeq+1,10000U);

			MutexLocker l(&qmEcho);
			qlEchoFrames.push_back(outbuff);
		}
	}
}
示例#3
0
long
cubeb_resampler_speex::fill(void * input_buffer, void * output_buffer, long frames_needed)
{
  // Use more input frames than strictly necessary, so in the worst case,
  // we have leftover unresampled frames at the end, that we can use
  // during the next iteration.
  assert(frames_needed <= buffer_frame_count);
  long before_resampling = frame_count_at_rate(frames_needed, resampling_ratio);
  long frames_requested = before_resampling - leftover_frame_count;

  // Copy the previous leftover frames to the front of the buffer.
  size_t leftover_bytes = frames_to_bytes(stream_params, leftover_frame_count);
  memcpy(resampling_src_buffer.get(), leftover_frames_buffer.get(), leftover_bytes);
  uint8_t * buffer_start = resampling_src_buffer.get() + leftover_bytes;

  long got = data_callback(stream, user_ptr, NULL, buffer_start, frames_requested);
  assert(got <= frames_requested);

  if (got < 0) {
    return CUBEB_ERROR;
  }

  uint32_t in_frames = leftover_frame_count + got;
  uint32_t out_frames = frames_needed;
  uint32_t old_in_frames = in_frames;

  if (stream_params.format == CUBEB_SAMPLE_FLOAT32NE) {
    float * in_buffer = reinterpret_cast<float *>(resampling_src_buffer.get());
    float * out_buffer = reinterpret_cast<float *>(output_buffer);
    speex_resampler_process_interleaved_float(speex_resampler, in_buffer, &in_frames,
                                              out_buffer, &out_frames);
  } else {
    short * in_buffer = reinterpret_cast<short *>(resampling_src_buffer.get());
    short * out_buffer = reinterpret_cast<short *>(output_buffer);
    speex_resampler_process_interleaved_int(speex_resampler, in_buffer, &in_frames,
                                            out_buffer, &out_frames);
  }

  // Copy the leftover frames to buffer for the next time.
  leftover_frame_count = old_in_frames - in_frames;
  assert(leftover_frame_count <= leftover_frame_size);

  size_t unresampled_bytes = frames_to_bytes(stream_params, leftover_frame_count);
  uint8_t * leftover_frames_start = resampling_src_buffer.get();
  leftover_frames_start += frames_to_bytes(stream_params, in_frames);
  memcpy(leftover_frames_buffer.get(), leftover_frames_start, unresampled_bytes);

  return out_frames;
}
示例#4
0
文件: speex.c 项目: 0xheart0/vlc
static block_t *Resample (filter_t *filter, block_t *in)
{
    SpeexResamplerState *st = (SpeexResamplerState *)filter->p_sys;

    const size_t framesize = filter->fmt_out.audio.i_bytes_per_frame;
    const unsigned irate = filter->fmt_in.audio.i_rate;
    const unsigned orate = filter->fmt_out.audio.i_rate;

    spx_uint32_t ilen = in->i_nb_samples;
    spx_uint32_t olen = ((ilen + 2) * orate * UINT64_C(11))
                      / (irate * UINT64_C(10));

    block_t *out = block_Alloc (olen * framesize);
    if (unlikely(out == NULL))
        goto error;

    speex_resampler_set_rate (st, irate, orate);

    int err;
    if (filter->fmt_in.audio.i_format == VLC_CODEC_FL32)
        err = speex_resampler_process_interleaved_float (st,
            (float *)in->p_buffer, &ilen, (float *)out->p_buffer, &olen);
    else
        err = speex_resampler_process_interleaved_int (st,
            (int16_t *)in->p_buffer, &ilen, (int16_t *)out->p_buffer, &olen);
    if (err != 0)
    {
        msg_Err (filter, "cannot resample: %s",
                 speex_resampler_strerror (err));
        block_Release (out);
        out = NULL;
        goto error;
    }

    if (ilen < in->i_nb_samples)
        msg_Err (filter, "lost %"PRIu32" of %u input frames",
                 in->i_nb_samples - ilen, in->i_nb_samples);

    out->i_buffer = olen * framesize;
    out->i_nb_samples = olen;
    out->i_pts = in->i_pts;
    out->i_length = olen * CLOCK_FREQ / filter->fmt_out.audio.i_rate;
error:
    block_Release (in);
    return out;
}
示例#5
0
void
Aulib::AudioResamplerSpeex::doResampling(float dst[], const float src[], size_t& dstLen,
                                         size_t& srcLen)
{
    if (not d->fResampler) {
        dstLen = srcLen = 0;
        return;
    }

    unsigned channels = currentChannels();
    spx_uint32_t spxInLen = srcLen / channels;
    spx_uint32_t spxOutLen = dstLen / channels;
    if (spxInLen == 0 or spxOutLen == 0) {
        dstLen = srcLen = 0;
        return;
    }
    speex_resampler_process_interleaved_float(d->fResampler.get(), src, &spxInLen, dst, &spxOutLen);
    dstLen = spxOutLen * channels;
    srcLen = spxInLen * channels;
}
示例#6
0
static void
cbjack_stream_refill(cubeb_stream * stream)
{
  unsigned int max_bytes = cbjack_stream_data_space(stream);
  long const max_num_output_frames = max_bytes / sizeof(float); // we're outputing floats
  long const max_num_frames = (max_num_output_frames * stream->params.rate) / stream->context->jack_sample_rate;

  long num_frames = stream->data_callback(stream,
                                          stream->user_ptr,
                                          NULL,
                                          stream->context->input_buffer,
                                          max_num_frames);

  // check for drain
  if (num_frames < max_num_frames)
    stream->state = STATE_DRAINING;

  float *interleaved_buffer;

  // convert 16-bit to float if needed
  if (stream->params.format == CUBEB_SAMPLE_S16NE) {
    s16ne_to_float(stream->context->float_interleaved_buffer, (short*)stream->context->input_buffer, num_frames * stream->params.channels);
    interleaved_buffer = stream->context->float_interleaved_buffer;
  } else if (stream->params.format == CUBEB_SAMPLE_FLOAT32NE) {
    interleaved_buffer = (float *)stream->context->input_buffer;
  }
  else {
    assert(0); // should not occur, checked by cbjack_stream_init
  }

  if (stream->resampler != NULL) {
    uint32_t resampler_consumed_frames = num_frames;
    uint32_t resampler_output_frames = (FIFO_SIZE / sizeof(float)) * MAX_CHANNELS * 3;

    int resampler_error = speex_resampler_process_interleaved_float(stream->resampler,
                                                                    interleaved_buffer,
                                                                    &resampler_consumed_frames,
                                                                    stream->context->resampled_interleaved_buffer,
                                                                    &resampler_output_frames);
    assert(resampler_error == 0);
    assert(resampler_consumed_frames == num_frames);
    num_frames = resampler_output_frames;
    interleaved_buffer = stream->context->resampled_interleaved_buffer;
  }

  // convert interleaved buffers to contigous buffers
  for (unsigned int c = 0; c < stream->params.channels; c++) {
    float* buffer = stream->context->buffer[c];
    for (long f = 0; f < num_frames; f++) {
      buffer[f] = interleaved_buffer[(f * stream->params.channels) + c] * stream->volume;
    }
  }

  // send contigous buffers to ring buffers
  for (unsigned int c = 0; c < stream->params.channels; c++) {
    size_t bytes_to_write = num_frames * sizeof(float);
    char* buffer = (char*)stream->context->buffer[c];
    while(bytes_to_write > 0) {
      size_t nwritten = api_jack_ringbuffer_write(stream->ringbuffer[c], buffer, bytes_to_write);
      bytes_to_write -= nwritten;
      buffer += nwritten;
      if (nwritten == 0) {
        assert(bytes_to_write == 0);
        break;
      }
    }
  }
}
示例#7
0
void AudioInput::addEcho(const void *data, unsigned int nsamp) {
	while (nsamp > 0) {
		// Make sure we don't overrun the echo frame buffer
		const unsigned int left = qMin(nsamp, iEchoLength - iEchoFilled);

		if (bEchoMulti) {
			const unsigned int samples = left * iEchoChannels;

			if (eEchoFormat == SampleFloat) {
				for (unsigned int i=0;i<samples;++i)
					pfEchoInput[i] = reinterpret_cast<const float *>(data)[i];
			}
			else {
				// 16bit PCM -> float
				for (unsigned int i=0;i<samples;++i)
					pfEchoInput[i] = static_cast<float>(reinterpret_cast<const short *>(data)[i]) * (1.0f / 32768.f);
			}
		} else {
			// Mix echo channels (converts 16bit PCM -> float if needed)
			imfEcho(pfEchoInput + iEchoFilled, data, left, iEchoChannels);
		}

		iEchoFilled += left;
		nsamp -= left;

		// If new samples are left offset data pointer to point at the first one for next iteration
		if (nsamp > 0) {
			if (eEchoFormat == SampleFloat)
				data = reinterpret_cast<const float *>(data) + left * iEchoChannels;
			else
				data = reinterpret_cast<const short *>(data) + left * iEchoChannels;
		}

		if (iEchoFilled == iEchoLength) {
			//Frame complete

			iEchoFilled = 0;

			// Resample if necessary
			float *ptr = srsEcho ? pfOutput : pfEchoInput;

			if (srsEcho) {
				spx_uint32_t inlen = iEchoLength;
				spx_uint32_t outlen = iFrameSize;
				speex_resampler_process_interleaved_float(srsEcho, pfEchoInput, &inlen, pfOutput, &outlen);
			}

			short *outbuff = new short[iEchoFrameSize];

			// float -> 16bit PCM
			const float mul = 32768.f;
			for (unsigned int j=0;j<iEchoFrameSize;++j)
				outbuff[j] = static_cast<short>(ptr[j] * mul);

			// Push frame into the echo chancellers jitter buffer
			QMutexLocker l(&qmEcho);

			iJitterSeq = qMin(iJitterSeq + 1,10000U);
			qlEchoFrames.append(outbuff);
		}
	}
}
opus_int64 audio_write(float *pcm, int channels, int frame_size, FILE *fout, SpeexResamplerState *resampler,
                       int *skip, shapestate *shapemem, int file, opus_int64 maxout)
{
   opus_int64 sampout=0;
   int i,ret,tmp_skip;
   unsigned out_len;
   short *out;
   float *buf;
   float *output;
   out=alloca(sizeof(short)*MAX_FRAME_SIZE*channels);
   buf=alloca(sizeof(float)*MAX_FRAME_SIZE*channels);
   maxout=maxout<0?0:maxout;
   do {
     if (skip){
       tmp_skip = (*skip>frame_size) ? (int)frame_size : *skip;
       *skip -= tmp_skip;
     } else {
       tmp_skip = 0;
     }
     if (resampler){
       unsigned in_len;
       output=buf;
       in_len = frame_size-tmp_skip;
       out_len = 1024<maxout?1024:maxout;
       speex_resampler_process_interleaved_float(resampler, pcm+channels*tmp_skip, &in_len, buf, &out_len);
       pcm += channels*(in_len+tmp_skip);
       frame_size -= in_len+tmp_skip;
     } else {
       output=pcm+channels*tmp_skip;
       out_len=frame_size-tmp_skip;
       frame_size=0;
     }

     /*Convert to short and save to output file*/
     if (shapemem){
       shape_dither_toshort(shapemem,out,output,out_len,channels);
     }else{
       for (i=0;i<(int)out_len*channels;i++)
         out[i]=(short)float2int(fmaxf(-32768,fminf(output[i]*32768.f,32767)));
     }
     if ((le_short(1)!=1)&&file){
       for (i=0;i<(int)out_len*channels;i++)
         out[i]=le_short(out[i]);
     }

     if(maxout>0)
     {
#if defined WIN32 || defined _WIN32
       if(!file){
         ret=WIN_Play_Samples (out, sizeof(short) * channels * (out_len<maxout?out_len:maxout));
         if(ret>0)ret/=sizeof(short)*channels;
         else fprintf(stderr, "Error playing audio.\n");
       }else
#elif defined HAVE_LIBSNDIO
       if(!file){
         ret=sio_write (hdl, out, sizeof(short) * channels * (out_len<maxout?out_len:maxout));
         if(ret>0)ret/=sizeof(short)*channels;
         else fprintf(stderr, "Error playing audio.\n");
       }else
#endif
         ret=fwrite(out, 2*channels, out_len<maxout?out_len:maxout, fout);
       sampout+=ret;
       maxout-=ret;
     }
   } while (frame_size>0 && maxout>0);
   return sampout;
}
示例#9
0
nsresult
OpusTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
{
  PROFILER_LABEL("OpusTrackEncoder", "GetEncodedTrack",
    js::ProfileEntry::Category::OTHER);
  {
    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
    // Wait until initialized or cancelled.
    while (!mCanceled && !mInitialized) {
      mReentrantMonitor.Wait();
    }
    if (mCanceled || mEncodingComplete) {
      return NS_ERROR_FAILURE;
    }
  }

  // calculation below depends on the truth that mInitialized is true.
  MOZ_ASSERT(mInitialized);

  // re-sampled frames left last time which didn't fit into an Opus packet duration.
  const int framesLeft = mResampledLeftover.Length() / mChannels;
  // When framesLeft is 0, (GetPacketDuration() - framesLeft) is a multiple
  // of kOpusSamplingRate. There is not precision loss in the integer division
  // in computing framesToFetch. If frameLeft > 0, we need to add 1 to
  // framesToFetch to ensure there will be at least n frames after re-sampling.
  const int frameRoundUp = framesLeft ? 1 : 0;

  MOZ_ASSERT(GetPacketDuration() >= framesLeft);
  // Try to fetch m frames such that there will be n frames
  // where (n + frameLeft) >= GetPacketDuration() after re-sampling.
  const int framesToFetch = !mResampler ? GetPacketDuration()
    : (GetPacketDuration() - framesLeft) * mSamplingRate / kOpusSamplingRate
      + frameRoundUp;
  {
    // Move all the samples from mRawSegment to mSourceSegment. We only hold
    // the monitor in this block.
    ReentrantMonitorAutoEnter mon(mReentrantMonitor);

    // Wait until enough raw data, end of stream or cancelled.
    while (!mCanceled && mRawSegment.GetDuration() +
        mSourceSegment.GetDuration() < framesToFetch &&
        !mEndOfStream) {
      mReentrantMonitor.Wait();
    }

    if (mCanceled || mEncodingComplete) {
      return NS_ERROR_FAILURE;
    }

    mSourceSegment.AppendFrom(&mRawSegment);

    // Pad |mLookahead| samples to the end of source stream to prevent lost of
    // original data, the pcm duration will be calculated at rate 48K later.
    if (mEndOfStream && !mEosSetInEncoder) {
      mEosSetInEncoder = true;
      mSourceSegment.AppendNullData(mLookahead);
    }
  }

  // Start encoding data.
  nsAutoTArray<AudioDataValue, 9600> pcm;
  pcm.SetLength(GetPacketDuration() * mChannels);
  AudioSegment::ChunkIterator iter(mSourceSegment);
  int frameCopied = 0;

  while (!iter.IsEnded() && frameCopied < framesToFetch) {
    AudioChunk chunk = *iter;

    // Chunk to the required frame size.
    int frameToCopy = chunk.GetDuration();
    if (frameCopied + frameToCopy > framesToFetch) {
      frameToCopy = framesToFetch - frameCopied;
    }

    if (!chunk.IsNull()) {
      // Append the interleaved data to the end of pcm buffer.
      AudioTrackEncoder::InterleaveTrackData(chunk, frameToCopy, mChannels,
        pcm.Elements() + frameCopied * mChannels);
    } else {
      memset(pcm.Elements() + frameCopied * mChannels, 0,
             frameToCopy * mChannels * sizeof(AudioDataValue));
    }

    frameCopied += frameToCopy;
    iter.Next();
  }

  RefPtr<EncodedFrame> audiodata = new EncodedFrame();
  audiodata->SetFrameType(EncodedFrame::OPUS_AUDIO_FRAME);
  int framesInPCM = frameCopied;
  if (mResampler) {
    nsAutoTArray<AudioDataValue, 9600> resamplingDest;
    // We want to consume all the input data, so we slightly oversize the
    // resampled data buffer so we can fit the output data in. We cannot really
    // predict the output frame count at each call.
    uint32_t outframes = frameCopied * kOpusSamplingRate / mSamplingRate + 1;
    uint32_t inframes = frameCopied;

    resamplingDest.SetLength(outframes * mChannels);

#if MOZ_SAMPLE_TYPE_S16
    short* in = reinterpret_cast<short*>(pcm.Elements());
    short* out = reinterpret_cast<short*>(resamplingDest.Elements());
    speex_resampler_process_interleaved_int(mResampler, in, &inframes,
                                                        out, &outframes);
#else
    float* in = reinterpret_cast<float*>(pcm.Elements());
    float* out = reinterpret_cast<float*>(resamplingDest.Elements());
    speex_resampler_process_interleaved_float(mResampler, in, &inframes,
                                                          out, &outframes);
#endif

    MOZ_ASSERT(pcm.Length() >= mResampledLeftover.Length());
    PodCopy(pcm.Elements(), mResampledLeftover.Elements(),
        mResampledLeftover.Length());

    uint32_t outframesToCopy = std::min(outframes,
        static_cast<uint32_t>(GetPacketDuration() - framesLeft));

    MOZ_ASSERT(pcm.Length() - mResampledLeftover.Length() >=
        outframesToCopy * mChannels);
    PodCopy(pcm.Elements() + mResampledLeftover.Length(),
        resamplingDest.Elements(), outframesToCopy * mChannels);
    int frameLeftover = outframes - outframesToCopy;
    mResampledLeftover.SetLength(frameLeftover * mChannels);
    PodCopy(mResampledLeftover.Elements(),
        resamplingDest.Elements() + outframesToCopy * mChannels,
        mResampledLeftover.Length());
    // This is always at 48000Hz.
    framesInPCM = framesLeft + outframesToCopy;
    audiodata->SetDuration(framesInPCM);
  } else {
    // The ogg time stamping and pre-skip is always timed at 48000.
    audiodata->SetDuration(frameCopied * (kOpusSamplingRate / mSamplingRate));
  }

  // Remove the raw data which has been pulled to pcm buffer.
  // The value of frameCopied should equal to (or smaller than, if eos)
  // GetPacketDuration().
  mSourceSegment.RemoveLeading(frameCopied);

  // Has reached the end of input stream and all queued data has pulled for
  // encoding.
  if (mSourceSegment.GetDuration() == 0 && mEndOfStream) {
    mEncodingComplete = true;
    LOG("[Opus] Done encoding.");
  }

  MOZ_ASSERT(mEndOfStream || framesInPCM == GetPacketDuration());

  // Append null data to pcm buffer if the leftover data is not enough for
  // opus encoder.
  if (framesInPCM < GetPacketDuration() && mEndOfStream) {
    PodZero(pcm.Elements() + framesInPCM * mChannels,
        (GetPacketDuration() - framesInPCM) * mChannels);
  }
  nsTArray<uint8_t> frameData;
  // Encode the data with Opus Encoder.
  frameData.SetLength(MAX_DATA_BYTES);
  // result is returned as opus error code if it is negative.
  int result = 0;
#ifdef MOZ_SAMPLE_TYPE_S16
  const opus_int16* pcmBuf = static_cast<opus_int16*>(pcm.Elements());
  result = opus_encode(mEncoder, pcmBuf, GetPacketDuration(),
                       frameData.Elements(), MAX_DATA_BYTES);
#else
  const float* pcmBuf = static_cast<float*>(pcm.Elements());
  result = opus_encode_float(mEncoder, pcmBuf, GetPacketDuration(),
                             frameData.Elements(), MAX_DATA_BYTES);
#endif
  frameData.SetLength(result >= 0 ? result : 0);

  if (result < 0) {
    LOG("[Opus] Fail to encode data! Result: %s.", opus_strerror(result));
  }
  if (mEncodingComplete) {
    if (mResampler) {
      speex_resampler_destroy(mResampler);
      mResampler = nullptr;
    }
    mResampledLeftover.SetLength(0);
  }

  audiodata->SwapInFrameData(frameData);
  aData.AppendEncodedFrame(audiodata);
  return result >= 0 ? NS_OK : NS_ERROR_FAILURE;
}
示例#10
0
int AudioResampler::FillBufferDifferentRate(uint8_t* buffer, int length) {
	const int input_samplesize = GetSamplesizeForFormat(input_format);
	const int output_samplesize = GetSamplesizeForFormat(output_format);
	//The buffer size has to be a multiple of a frame
	const int buffer_size=sizeof(internal_buffer) - sizeof(internal_buffer)%(nr_of_channels*((input_samplesize>output_samplesize) ? input_samplesize : output_samplesize));
	
	int total_output_frames = length / (output_samplesize*nr_of_channels);
	int amount_of_samples_to_read = 0;
	int amount_of_samples_read = 0;
	
	uint8_t * advanced_input_buffer = internal_buffer;
	int unused_frames = 0;
	int empty_buffer_space = 0;
	int error = 0;
	
	#ifdef HAVE_LIBSPEEXDSP
		spx_uint32_t numerator = 0;
		spx_uint32_t denominator = 0;
	#endif

	while (total_output_frames > 0) {
		//Calculate how much frames of the last cycle are unused - to reuse them
		unused_frames = conversion_data.input_frames - conversion_data.input_frames_used;
		empty_buffer_space = buffer_size / output_samplesize - unused_frames*nr_of_channels;
		
		advanced_input_buffer = internal_buffer;

		//If there is still unused data in the input_buffer order it to the front
		for (int i = 0; i < unused_frames*nr_of_channels*output_samplesize; i++) {
			*advanced_input_buffer = *(advanced_input_buffer + empty_buffer_space*output_samplesize);
			advanced_input_buffer++;
		}
		//advanced_input_buffer is now offset to the first frame of new data!

		//ensure that the input buffer is not able to overrun
		amount_of_samples_to_read = (input_samplesize > output_samplesize) ? (empty_buffer_space*output_samplesize) / input_samplesize : empty_buffer_space;

		//Read as many frames as needed to refill the buffer (filled after the conversion to float)
		if (amount_of_samples_to_read != 0) {
			switch (output_format) {
				case AudioDecoder::Format::F32: amount_of_samples_read = DecodeAndConvertFloat(wrapped_decoder.get(), advanced_input_buffer, amount_of_samples_to_read, input_samplesize, input_format); break;
			#ifdef HAVE_LIBSPEEXDSP
				case AudioDecoder::Format::S16:  amount_of_samples_read = DecodeAndConvertInt16(wrapped_decoder.get(), advanced_input_buffer, amount_of_samples_to_read, input_samplesize, input_format); break;
			#endif
				default: error_message = "internal error: output_format is not convertable"; return ERROR;
			}
			if (amount_of_samples_read < 0) {
				error_message = wrapped_decoder->GetError();
				return amount_of_samples_read; //error occured
			}
		}
		//Now we have a prepared full buffer of converted values

		//Prepare the source data
		conversion_data.input_frames = amount_of_samples_read / nr_of_channels + unused_frames;
		conversion_data.output_frames = total_output_frames;

		#if defined(HAVE_LIBSPEEXDSP)
			conversion_data.input_frames_used = conversion_data.input_frames;
			conversion_data.output_frames_gen = conversion_data.output_frames;

			//libspeexdsp defines a sample rate conversion with a fraction (input/output)
			numerator = input_rate*pitch;
			denominator = output_rate * STANDARD_PITCH;
			if (pitch_handled_by_decoder) {
				numerator = input_rate;
				denominator = output_rate;
			}
			if (conversion_data.ratio_num != numerator || conversion_data.ratio_denom != denominator) {
				speex_resampler_set_rate_frac(conversion_state, numerator, denominator, input_rate, output_rate);
				conversion_data.ratio_num = numerator;
				conversion_data.ratio_denom = denominator;
			}
			
			//A pitfall from libspeexdsp if the output buffer is defined to big - everything stutters -achieved good values with the same size as the input buffer for a maximum
			conversion_data.output_frames_gen=(conversion_data.input_frames<conversion_data.output_frames_gen) ? conversion_data.input_frames :conversion_data.output_frames_gen;
			
			switch (output_format) {
			case Format::F32:
				error = speex_resampler_process_interleaved_float(conversion_state, (float*)internal_buffer, &conversion_data.input_frames_used, (float*)buffer, &conversion_data.output_frames_gen);
				break;
			case Format::S16:
				error = speex_resampler_process_interleaved_int(conversion_state, (spx_int16_t*)internal_buffer, &conversion_data.input_frames_used, (spx_int16_t*)buffer, &conversion_data.output_frames_gen);
				break;
			default: error_message = "internal error: output_format is not convertable"; return ERROR;
			}
			
			if (error != 0) {
				error_message = speex_resampler_strerror(error);
				return ERROR;
			}
		#elif defined(HAVE_LIBSAMPLERATE)
			conversion_data.data_in = (float*)internal_buffer;
			conversion_data.data_out = (float*)buffer;
			if (pitch_handled_by_decoder) {
				conversion_data.src_ratio = (output_rate*1.0) / input_rate;
			}
			else {
				conversion_data.src_ratio = (output_rate*STANDARD_PITCH *1.0) / (input_rate*pitch*1.0);
			}
			conversion_data.end_of_input = (wrapped_decoder->IsFinished()) ? 1 : 0;

			//Now let libsamplerate filter the data
			error = src_process(conversion_state, &conversion_data);

			if (error != 0) {
				error_message = src_strerror(error);
				return ERROR;
			}
		#endif

		total_output_frames -= conversion_data.output_frames_gen;
		buffer += conversion_data.output_frames_gen*nr_of_channels*output_samplesize;

		if ((conversion_data.input_frames == 0 && conversion_data.output_frames_gen <= conversion_data.output_frames) || conversion_data.output_frames_gen == 0) {
			finished = true;
			//There is nothing left to convert - return how much samples (in bytes) are converted! 
			return length - total_output_frames*(output_samplesize*nr_of_channels);
		}
	}
	return length;
}
示例#11
0
int main(int argc, char **argv) {

	CALLGRIND_STOP_INSTRUMENTATION;
	CALLGRIND_ZERO_STATS;

	QCoreApplication a(argc, argv);

	QFile f((argc >= 2) ? argv[1] : "wb_male.wav");
	if (! f.open(QIODevice::ReadOnly)) {
		qFatal("Failed to open file!");
	}
	f.seek(36 + 8);

	QFile o("output.raw");
	if (!(RUNNING_ON_VALGRIND))
		if (! o.open(QIODevice::WriteOnly))
			qFatal("Failed to open output!");

	QFile vf((argc >= 3) ? argv[2] : "verify.raw");
	if (! vf.open(QIODevice::ReadOnly)) {
		qWarning("Failed to open validate file!");
	}

	QDataStream out(&o);
	QDataStream verify(&vf);

	const int iFrameSize = 320;

	QVector<QByteArray> v;
	while (1) {
		QByteArray qba = f.read(iFrameSize * 2);
		if (qba.size() != iFrameSize * 2)
			break;
		v.append(qba);
	}

	int nframes = v.size();


	qWarning("Ready to process %d frames of %d samples", nframes, iFrameSize);

	QVector<short *> qvInShort;
	QVector<float *> qvIn;
	QVector<float *> qvDirect;
	QVector<float *> qvInterpolate;
	QVector<float *> qvInterpolateMC;
	QVector<short *> qvInterpolateShort;
	QVector<float *> qv8;
	QVector<float *> qv96;

	const float sfraq1 = tfreq1 / 16000.0f;
	float fOutSize1 = iFrameSize * sfraq1;
	int iOutSize1 = lroundf(fOutSize1);

	const float sfraq2 = tfreq2 / 16000.0f;
	float fOutSize2 = iFrameSize * sfraq2;
	int iOutSize2 = lroundf(fOutSize2);

	int iOutSize8 = iFrameSize / 2;
	int iOutSize96 = iFrameSize * 6;

	if (RUNNING_ON_VALGRIND)
		nframes = qMin(nframes, 10);

	QVector<float> fInput(nframes * iFrameSize);
	QVector<float> fDirect(nframes * iOutSize1);
	QVector<float> fInterpolate(nframes * iOutSize2);
	QVector<float> fInterpolateMC(nframes * iOutSize2);
	QVector<short> sInterpolate(nframes * iOutSize2);
	QVector<float> f96(nframes * iOutSize96);
	QVector<float> f8(nframes *iOutSize8);

	for (int i=0;i<nframes;i++) {
		short *s = reinterpret_cast<short *>(v[i].data());
		float *f = fInput.data() + i * iFrameSize;

		for (int j=0;j<iFrameSize;j++)
			f[j]=s[j]+20;

		qvInShort.append(s);
		qvIn.append(f);
		qvDirect.append(fDirect.data() + i * iOutSize1);
		qvInterpolate.append(fInterpolate.data() + i * iOutSize2);
		qvInterpolateMC.append(fInterpolateMC.data() + i * iOutSize2);
		qvInterpolateShort.append(sInterpolate.data() + i * iOutSize2);
		qv8.append(f8.data() + i * iOutSize8);
		qv96.append(f96.data() + i * iOutSize96);
	}

	int err;
	SpeexResamplerState *srs1 = speex_resampler_init(1, 16000, lroundf(tfreq1), qual, &err);
	SpeexResamplerState *srs2 = speex_resampler_init(1, 16000, lroundf(tfreq2), qual, &err);
	SpeexResamplerState *srs2i = speex_resampler_init(1, 16000, lroundf(tfreq2), qual, &err);
	SpeexResamplerState *srss = speex_resampler_init(3, 16000, lroundf(tfreq2), qual, &err);

	SpeexResamplerState *srsto96 = speex_resampler_init(1, 16000, 96000, 5, &err);
	SpeexResamplerState *srs8to96 = speex_resampler_init(1, 8000, 96000, qual, &err);
	SpeexResamplerState *srs96to8 = speex_resampler_init(1, 96000, 8000, qual, &err);


#ifdef Q_OS_WIN
	if (!SetPriorityClass(GetCurrentProcess(),REALTIME_PRIORITY_CLASS))
		qWarning("Application: Failed to set priority!");
#endif

	int len;
	spx_uint32_t inlen;
	spx_uint32_t outlen;

	Timer t;
	quint64 e;

	if (! RUNNING_ON_VALGRIND) {
#ifndef Q_OS_WIN
		struct sched_param sp;
		sp.sched_priority = sched_get_priority_max(SCHED_FIFO);

		cpu_set_t cpuset;
		CPU_ZERO(&cpuset);
		CPU_SET(1, &cpuset);

		if (sched_setscheduler(getpid(), SCHED_FIFO, &sp) != 0)
			qWarning("No realtime.");
		if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0)
			qWarning("No mlock.");
		if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)
			qWarning("No affinity");

		sched_yield();
#endif

		for (int i=0;i<nframes;++i) {
			inlen = iFrameSize;
			outlen = iOutSize96;
			speex_resampler_process_float(srsto96, 0, qvIn[i], &inlen, qv96[i], &outlen);
		}

		QVector<unsigned long long> qvTimes;
		QPair<unsigned long long, unsigned long long> ci;

		for (int j=0;j<loops;j++) {
			t.restart();
			for (int i=0;i<nframes;i++) {
				inlen = iFrameSize;
				outlen = iOutSize1;
				speex_resampler_process_float(srs1, 0, qvIn[i], &inlen, qvDirect[i], &outlen);
			}
			e = t.elapsed();
			qvTimes.append(e);
		}
		ci = confint(qvTimes);
		qWarning("Direct:      %8llu +/- %3llu usec (%d)", ci.first, ci.second, qvTimes.count(), qvTimes.count());

		qvTimes.clear();

		for (int j=0;j<loops;j++) {
			t.restart();
			for (int i=0;i<nframes;i++) {
				inlen = iFrameSize;
				outlen = iOutSize2;
				speex_resampler_process_float(srs2, 0, qvIn[i], &inlen, qvInterpolate[i], &outlen);
			}
			e = t.elapsed();
			qvTimes.append(e);
		}
		ci = confint(qvTimes);
		qWarning("Interpolate: %8llu +/- %3llu usec (%d)", ci.first, ci.second, qvTimes.count());

		qvTimes.clear();
		for (int j=0;j<loops;j++) {
			t.restart();
			for (int i=0;i<nframes;i++) {
				inlen = iOutSize96;
				outlen = iOutSize8;
				speex_resampler_process_float(srs96to8, 0, qv96[i], &inlen, qv8[i], &outlen);
			}
			e = t.elapsed();
			qvTimes.append(e);
		}
		ci = confint(qvTimes);
		qWarning("96 => 8:     %8llu +/- %3llu usec (%d)", ci.first, ci.second, qvTimes.count());

		qvTimes.clear();
		t.restart();
		for (int j=0;j<loops;j++) {
			t.restart();
			for (int i=0;i<nframes;i++) {
				inlen = iOutSize8;
				outlen = iOutSize96;
				speex_resampler_process_float(srs8to96, 0, qv8[i], &inlen, qv96[i], &outlen);
			}
			e = t.elapsed();
			qvTimes.append(e);
		}
		ci = confint(qvTimes);
		qWarning("8 => 96:     %8llu +/- %3llu usec (%d)", ci.first, ci.second, qvTimes.count());

		speex_resampler_reset_mem(srs1);
		speex_resampler_reset_mem(srs2);
	}

	t.restart();
	CALLGRIND_START_INSTRUMENTATION;

	for (int i=0;i<nframes;i++) {
		inlen = iFrameSize;
		outlen = iOutSize1;
		speex_resampler_process_float(srs1, 0, qvIn[i], &inlen, qvDirect[i], &outlen);

		inlen = iFrameSize;
		outlen = iOutSize2;
		speex_resampler_process_float(srs2, 0, qvIn[i], &inlen, qvInterpolate[i], &outlen);

		inlen = iFrameSize;
		outlen = iOutSize2;
		speex_resampler_process_int(srs2i, 0, qvInShort[i], &inlen, qvInterpolateShort[i], &outlen);

		inlen = iFrameSize / 4;
		outlen = iOutSize2 / 4;
		speex_resampler_process_interleaved_float(srss, qvIn[i], &inlen, qvInterpolateMC[i], &outlen);
	}
	e = t.elapsed();

#ifdef Q_OS_WIN
	if (!SetPriorityClass(GetCurrentProcess(),NORMAL_PRIORITY_CLASS))
		qWarning("Application: Failed to reset priority!");
#endif

	const int freq[10] = { 22050, 32000, 11025, 16000, 48000, 41000, 8000, 96000, 11025, 1600 };

	QVector<float> fMagic;

	for (int f=0;f<10;f++) {
		float fbuff[32767];
		speex_resampler_set_rate(srs1, 16000, freq[f]);
		for (int q = 0;q < 10;q++) {
			speex_resampler_set_quality(srs1, (3*q) % 7);
			inlen = iFrameSize;
			outlen = 32767;
			speex_resampler_process_float(srs1, 0, qvIn[(f*10+q) % nframes], &inlen, fbuff, &outlen);
			for (int j=0;j<outlen;j++)
				fMagic.append(fbuff[j]);
		}
		inlen = iFrameSize;
		outlen = 32767;
		speex_resampler_process_float(srs1, 0, NULL, &inlen, fbuff, &outlen);
		for (int j=0;j<outlen;j++)
			fMagic.append(fbuff[j]);
	}

	// Cropped magic test
	for (int f=0;f<10;f++) {
		float fbuff[32767];
		speex_resampler_set_rate(srs1, 16000, freq[f]);
		for (int q = 0;q < 10;q++) {
			speex_resampler_set_quality(srs1, (3*q) % 7);
			inlen = iFrameSize;
			outlen = 16;
			speex_resampler_process_float(srs1, 0, qvIn[(f*10+q) % nframes], &inlen, fbuff, &outlen);
			for (int j=0;j<outlen;j++)
				fMagic.append(fbuff[j]);
		}
		inlen = iFrameSize;
		outlen = 32767;
		speex_resampler_process_float(srs1, 0, NULL, &inlen, fbuff, &outlen);
		for (int j=0;j<outlen;j++)
			fMagic.append(fbuff[j]);
	}


	CALLGRIND_STOP_INSTRUMENTATION;

	qWarning("Used %llu usec", e);
	qWarning("%.2f times realtime", (20000ULL * nframes) / (e * 1.0));

	if (! RUNNING_ON_VALGRIND) {
		QVector<float> vDirect;
		QVector<float> vInterpolate;
		QVector<short> vsInterpolate;
		QVector<float> vMagic;
		QVector<float> vInterpolateMC;

		out << fDirect << fInterpolate << sInterpolate << fMagic << fInterpolateMC;

		if (vf.isOpen()) {
			verify >> vDirect >> vInterpolate >> vsInterpolate >> vMagic >> vInterpolateMC;

			double rmsd = veccomp(vDirect, fDirect, "SRS1");
			double rmsi = veccomp(vInterpolate, fInterpolate, "SRS2");
			veccomp(vsInterpolate, sInterpolate, "SRS2i");
			veccomp(vMagic, fMagic, "Magic");
			veccomp(vInterpolateMC, fInterpolateMC, "MC");
			qWarning("Direct: %g", rmsd);
			qWarning("Interp: %g", rmsi);
		} else {
示例#12
0
/** @internal @This handles data.
 *
 * @param upipe description structure of the pipe
 * @param uref uref structure
 * @param upump_p reference to pump that generated the buffer
 * @return false if the input must be blocked
 */
static bool upipe_speexdsp_handle(struct upipe *upipe, struct uref *uref,
                             struct upump **upump_p)
{
    struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe);

    struct urational drift_rate;
    if (!ubase_check(uref_clock_get_rate(uref, &drift_rate)))
        drift_rate = (struct urational){ 1, 1 };

    /* reinitialize resampler when drift rate changes */
    if (urational_cmp(&drift_rate, &upipe_speexdsp->drift_rate)) {
        upipe_speexdsp->drift_rate = drift_rate;
        spx_uint32_t ratio_num = drift_rate.den;
        spx_uint32_t ratio_den = drift_rate.num;
        spx_uint32_t in_rate = upipe_speexdsp->rate * ratio_num / ratio_den;
        spx_uint32_t out_rate = upipe_speexdsp->rate;
        int err = speex_resampler_set_rate_frac(upipe_speexdsp->ctx,
                ratio_num, ratio_den, in_rate, out_rate);
        if (err) {
            upipe_err_va(upipe, "Couldn't resample from %u to %u: %s",
                in_rate, out_rate, speex_resampler_strerror(err));
        } else {
            upipe_dbg_va(upipe, "Resampling from %u to %u",
                in_rate, out_rate);
        }
    }

    size_t size;
    if (!ubase_check(uref_sound_size(uref, &size, NULL /* sample_size */))) {
        uref_free(uref);
        return true;
    }

    struct ubuf *ubuf = ubuf_sound_alloc(upipe_speexdsp->ubuf_mgr, size + 10);
    if (!ubuf)
        return false;

    const void *in;
    uref_sound_read_void(uref, 0, -1, &in, 1);

    void *out;
    ubuf_sound_write_void(ubuf, 0, -1, &out, 1);

    spx_uint32_t in_len = size;         /* input size */
    spx_uint32_t out_len = size + 10;   /* available output size */

    int err;

    if (upipe_speexdsp->f32)
        err = speex_resampler_process_interleaved_float(upipe_speexdsp->ctx,
                in, &in_len, out, &out_len);
    else
        err = speex_resampler_process_interleaved_int(upipe_speexdsp->ctx,
                in, &in_len, out, &out_len);

    if (err) {
        upipe_err_va(upipe, "Could not resample: %s",
                speex_resampler_strerror(err));
    }

    uref_sound_unmap(uref, 0, -1, 1);
    ubuf_sound_unmap(ubuf, 0, -1, 1);

    if (err) {
        ubuf_free(ubuf);
    } else {
        ubuf_sound_resize(ubuf, 0, out_len);
        uref_attach_ubuf(uref, ubuf);
    }

    upipe_speexdsp_output(upipe, uref, upump_p);
    return true;
}

/** @internal @This receives incoming uref.
 *
 * @param upipe description structure of the pipe
 * @param uref uref structure describing the picture
 * @param upump_p reference to pump that generated the buffer
 */
static void upipe_speexdsp_input(struct upipe *upipe, struct uref *uref,
                            struct upump **upump_p)
{
    if (!upipe_speexdsp_check_input(upipe)) {
        upipe_speexdsp_hold_input(upipe, uref);
        upipe_speexdsp_block_input(upipe, upump_p);
    } else if (!upipe_speexdsp_handle(upipe, uref, upump_p)) {
        upipe_speexdsp_hold_input(upipe, uref);
        upipe_speexdsp_block_input(upipe, upump_p);
        /* Increment upipe refcount to avoid disappearing before all packets
         * have been sent. */
        upipe_use(upipe);
    }
}

/** @internal @This receives a provided ubuf manager.
 *
 * @param upipe description structure of the pipe
 * @param flow_format amended flow format
 * @return an error code
 */
static int upipe_speexdsp_check(struct upipe *upipe, struct uref *flow_format)
{
    struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe);
    if (flow_format != NULL)
        upipe_speexdsp_store_flow_def(upipe, flow_format);

    if (upipe_speexdsp->flow_def == NULL)
        return UBASE_ERR_NONE;

    bool was_buffered = !upipe_speexdsp_check_input(upipe);
    upipe_speexdsp_output_input(upipe);
    upipe_speexdsp_unblock_input(upipe);
    if (was_buffered && upipe_speexdsp_check_input(upipe)) {
        /* All packets have been output, release again the pipe that has been
         * used in @ref upipe_speexdsp_input. */
        upipe_release(upipe);
    }
    return UBASE_ERR_NONE;
}

/** @internal @This sets the input flow definition.
 *
 * @param upipe description structure of the pipe
 * @param flow_def flow definition packet
 * @return an error code
 */
static int upipe_speexdsp_set_flow_def(struct upipe *upipe, struct uref *flow_def)
{
    struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe);

    if (flow_def == NULL)
        return UBASE_ERR_INVALID;

    const char *def;
    UBASE_RETURN(uref_flow_get_def(flow_def, &def))

    if (unlikely(ubase_ncmp(def, "sound.f32.") &&
                ubase_ncmp(def, "sound.s16.")))
        return UBASE_ERR_INVALID;

    uint8_t in_planes;
    if (unlikely(!ubase_check(uref_sound_flow_get_planes(flow_def,
                                                         &in_planes))))
        return UBASE_ERR_INVALID;

    if (in_planes != 1) {
        upipe_err(upipe, "only interleaved audio is supported");
        return UBASE_ERR_INVALID;
    }

    if (!ubase_check(uref_sound_flow_get_rate(flow_def,
                    &upipe_speexdsp->rate))) {
        upipe_err(upipe, "no sound rate defined");
        uref_dump(flow_def, upipe->uprobe);
        return UBASE_ERR_INVALID;
    }

    uint8_t channels;
    if (unlikely(!ubase_check(uref_sound_flow_get_channels(flow_def,
                        &channels))))
        return UBASE_ERR_INVALID;

    flow_def = uref_dup(flow_def);
    if (unlikely(flow_def == NULL)) {
        upipe_throw_fatal(upipe, UBASE_ERR_ALLOC);
        return UBASE_ERR_ALLOC;
    }

    upipe_speexdsp_require_ubuf_mgr(upipe, flow_def);

    if (upipe_speexdsp->ctx)
        speex_resampler_destroy(upipe_speexdsp->ctx);

    upipe_speexdsp->f32 = !ubase_ncmp(def, "sound.f32.");

    int err;
    upipe_speexdsp->ctx = speex_resampler_init(channels,
                upipe_speexdsp->rate, upipe_speexdsp->rate,
                upipe_speexdsp->quality, &err);
    if (!upipe_speexdsp->ctx) {
        upipe_err_va(upipe, "Could not create resampler: %s",
                speex_resampler_strerror(err));
        return UBASE_ERR_INVALID;
    }

    return UBASE_ERR_NONE;
}

/** @internal @This provides a flow format suggestion.
 *
 * @param upipe description structure of the pipe
 * @param request description structure of the request
 * @return an error code
 */
static int upipe_speexdsp_provide_flow_format(struct upipe *upipe,
                                          struct urequest *request)
{
    const char *def;
    UBASE_RETURN(uref_flow_get_def(request->uref, &def))
    uint8_t channels;
    UBASE_RETURN(uref_sound_flow_get_channels(request->uref, &channels))
    uint8_t planes;
    UBASE_RETURN(uref_sound_flow_get_planes(request->uref, &planes))
    uint8_t sample_size;
    UBASE_RETURN(uref_sound_flow_get_sample_size(request->uref, &sample_size))

    struct uref *flow = uref_dup(request->uref);
    UBASE_ALLOC_RETURN(flow);

    uref_sound_flow_clear_format(flow);
    uref_sound_flow_set_planes(flow, 0);
    uref_sound_flow_set_channels(flow, channels);
    uref_sound_flow_add_plane(flow, "all");
    if (ubase_ncmp(def, "sound.s16.")) {
        uref_flow_set_def(flow, "sound.f32."); /* prefer f32 over s16 */
        uref_sound_flow_set_sample_size(flow, 4 * channels);
    } else {
        uref_flow_set_def(flow, def);
        uref_sound_flow_set_sample_size(flow, (planes > 1) ? sample_size :
                sample_size / channels);
    }

    return urequest_provide_flow_format(request, flow);
}

/** @internal @This processes control commands on a speexdsp pipe.
 *
 * @param upipe description structure of the pipe
 * @param command type of command to process
 * @param args arguments of the command
 * @return an error code
 */
static int upipe_speexdsp_control(struct upipe *upipe, int command, va_list args)
{
    struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe);

    switch (command) {
        /* generic commands */
        case UPIPE_REGISTER_REQUEST: {
            struct urequest *request = va_arg(args, struct urequest *);
            if (request->type == UREQUEST_FLOW_FORMAT)
                return upipe_speexdsp_provide_flow_format(upipe, request);
            if (request->type == UREQUEST_UBUF_MGR)
                return upipe_throw_provide_request(upipe, request);
            return upipe_speexdsp_alloc_output_proxy(upipe, request);
        }
        case UPIPE_UNREGISTER_REQUEST: {
            struct urequest *request = va_arg(args, struct urequest *);
            if (request->type == UREQUEST_FLOW_FORMAT ||
                request->type == UREQUEST_UBUF_MGR)
                return UBASE_ERR_NONE;
            return upipe_speexdsp_free_output_proxy(upipe, request);
        }

        case UPIPE_GET_OUTPUT: {
            struct upipe **p = va_arg(args, struct upipe **);
            return upipe_speexdsp_get_output(upipe, p);
        }
        case UPIPE_SET_OUTPUT: {
            struct upipe *output = va_arg(args, struct upipe *);
            return upipe_speexdsp_set_output(upipe, output);
        }
        case UPIPE_GET_FLOW_DEF: {
            struct uref **p = va_arg(args, struct uref **);
            return upipe_speexdsp_get_flow_def(upipe, p);
        }
        case UPIPE_SET_FLOW_DEF: {
            struct uref *flow = va_arg(args, struct uref *);
            return upipe_speexdsp_set_flow_def(upipe, flow);
        }
        case UPIPE_SET_OPTION: {
            const char *option = va_arg(args, const char *);
            const char *value  = va_arg(args, const char *);
            if (strcmp(option, "quality"))
                return UBASE_ERR_INVALID;
            if (upipe_speexdsp->ctx)
                return UBASE_ERR_BUSY;
            int quality = atoi(value);
            if (quality > SPEEX_RESAMPLER_QUALITY_MAX) {
                quality = SPEEX_RESAMPLER_QUALITY_MAX;
                upipe_err_va(upipe, "Clamping quality to %d",
                        SPEEX_RESAMPLER_QUALITY_MAX);
            } else if (quality < SPEEX_RESAMPLER_QUALITY_MIN) {
                quality = SPEEX_RESAMPLER_QUALITY_MIN;
                upipe_err_va(upipe, "Clamping quality to %d",
                        SPEEX_RESAMPLER_QUALITY_MIN);
            }
            upipe_speexdsp->quality = quality;
            return UBASE_ERR_NONE;
        }

        default:
            return UBASE_ERR_UNHANDLED;
    }
}

/** @internal @This allocates a speexdsp pipe.
 *
 * @param mgr common management structure
 * @param uprobe structure used to raise events
 * @param signature signature of the pipe allocator
 * @param args optional arguments
 * @return pointer to upipe or NULL in case of allocation error
 */
static struct upipe *upipe_speexdsp_alloc(struct upipe_mgr *mgr,
                                     struct uprobe *uprobe,
                                     uint32_t signature, va_list args)
{
    struct upipe *upipe = upipe_speexdsp_alloc_void(mgr, uprobe, signature,
                                               args);
    if (unlikely(upipe == NULL))
        return NULL;

    struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe);

    upipe_speexdsp->ctx = NULL;
    upipe_speexdsp->drift_rate = (struct urational){ 0, 0 };
    upipe_speexdsp->quality = SPEEX_RESAMPLER_QUALITY_MAX;

    upipe_speexdsp_init_urefcount(upipe);
    upipe_speexdsp_init_ubuf_mgr(upipe);
    upipe_speexdsp_init_output(upipe);
    upipe_speexdsp_init_flow_def(upipe);
    upipe_speexdsp_init_input(upipe);

    upipe_throw_ready(upipe);
    return upipe;
}

/** @This frees a upipe.
 *
 * @param upipe description structure of the pipe
 */
static void upipe_speexdsp_free(struct upipe *upipe)
{
    struct upipe_speexdsp *upipe_speexdsp = upipe_speexdsp_from_upipe(upipe);
    if (likely(upipe_speexdsp->ctx))
        speex_resampler_destroy(upipe_speexdsp->ctx);

    upipe_throw_dead(upipe);
    upipe_speexdsp_clean_input(upipe);
    upipe_speexdsp_clean_output(upipe);
    upipe_speexdsp_clean_flow_def(upipe);
    upipe_speexdsp_clean_ubuf_mgr(upipe);
    upipe_speexdsp_clean_urefcount(upipe);
    upipe_speexdsp_free_void(upipe);
}

/** module manager static descriptor */
static struct upipe_mgr upipe_speexdsp_mgr = {
    .refcount = NULL,
    .signature = UPIPE_SPEEXDSP_SIGNATURE,

    .upipe_alloc = upipe_speexdsp_alloc,
    .upipe_input = upipe_speexdsp_input,
    .upipe_control = upipe_speexdsp_control,

    .upipe_mgr_control = NULL
};

/** @This returns the management structure for speexdsp pipes
 *
 * @return pointer to manager
 */
struct upipe_mgr *upipe_speexdsp_mgr_alloc(void)
{
    return &upipe_speexdsp_mgr;
}