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); } }
//存储回音数据 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); } } }
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; }
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; }
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; }
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; } } } }
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; }
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; }
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; }
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 {
/** @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; }