int create_resampler(uint32_t inSampleRate, uint32_t outSampleRate, uint32_t channelCount, uint32_t quality, struct resampler_buffer_provider* provider, struct resampler_itfe **resampler) { int error; struct resampler *rsmp; ALOGV("create_resampler() In SR %d Out SR %d channels %d", inSampleRate, outSampleRate, channelCount); if (resampler == NULL) { return -EINVAL; } *resampler = NULL; if (quality <= RESAMPLER_QUALITY_MIN || quality >= RESAMPLER_QUALITY_MAX) { return -EINVAL; } rsmp = (struct resampler *)calloc(1, sizeof(struct resampler)); rsmp->speex_resampler = speex_resampler_init(channelCount, inSampleRate, outSampleRate, quality, &error); if (rsmp->speex_resampler == NULL) { ALOGW("ReSampler: Cannot create speex resampler: %s", speex_resampler_strerror(error)); free(rsmp); return -ENODEV; } rsmp->itfe.reset = resampler_reset; rsmp->itfe.resample_from_provider = resampler_resample_from_provider; rsmp->itfe.resample_from_input = resampler_resample_from_input; rsmp->itfe.delay_ns = resampler_delay_ns; rsmp->provider = provider; rsmp->in_sample_rate = inSampleRate; rsmp->out_sample_rate = outSampleRate; rsmp->channel_count = channelCount; rsmp->in_buf = NULL; rsmp->in_buf_size = 0; resampler_reset(&rsmp->itfe); int frames = speex_resampler_get_input_latency(rsmp->speex_resampler); rsmp->speex_delay_ns = (int32_t)((1000000000 * (int64_t)frames) / rsmp->in_sample_rate); frames = speex_resampler_get_output_latency(rsmp->speex_resampler); rsmp->speex_delay_ns += (int32_t)((1000000000 * (int64_t)frames) / rsmp->out_sample_rate); *resampler = &rsmp->itfe; ALOGV("create_resampler() DONE rsmp %p &rsmp->itfe %p speex %p", rsmp, &rsmp->itfe, rsmp->speex_resampler); return 0; }
void MediaDecodeTask::Decode() { MOZ_ASSERT(!mThreadPool == NS_IsMainThread(), "We should be on the main thread only if we don't have a thread pool"); mBufferDecoder->BeginDecoding(NS_GetCurrentThread()); // Tell the decoder reader that we are not going to play the data directly, // and that we should not reject files with more channels than the audio // bakend support. mDecoderReader->SetIgnoreAudioOutputFormat(); mDecoderReader->OnDecodeThreadStart(); MediaInfo mediaInfo; nsAutoPtr<MetadataTags> tags; nsresult rv = mDecoderReader->ReadMetadata(&mediaInfo, getter_Transfers(tags)); if (NS_FAILED(rv)) { ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent); return; } if (!mDecoderReader->HasAudio()) { ReportFailureOnMainThread(WebAudioDecodeJob::NoAudio); return; } while (mDecoderReader->DecodeAudioData()) { // consume all of the buffer continue; } mDecoderReader->OnDecodeThreadFinish(); MediaQueue<AudioData>& audioQueue = mDecoderReader->AudioQueue(); uint32_t frameCount = audioQueue.FrameCount(); uint32_t channelCount = mediaInfo.mAudio.mChannels; uint32_t sampleRate = mediaInfo.mAudio.mRate; if (!frameCount || !channelCount || !sampleRate) { ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent); return; } const uint32_t destSampleRate = mDecodeJob.mContext->SampleRate(); AutoResampler resampler; uint32_t resampledFrames = frameCount; if (sampleRate != destSampleRate) { resampledFrames = static_cast<uint32_t>( static_cast<uint64_t>(destSampleRate) * static_cast<uint64_t>(frameCount) / static_cast<uint64_t>(sampleRate) ); resampler = speex_resampler_init(channelCount, sampleRate, destSampleRate, SPEEX_RESAMPLER_QUALITY_DEFAULT, nullptr); speex_resampler_skip_zeros(resampler); resampledFrames += speex_resampler_get_output_latency(resampler); } // Allocate the channel buffers. Note that if we end up resampling, we may // write fewer bytes than mResampledFrames to the output buffer, in which // case mWriteIndex will tell us how many valid samples we have. static const fallible_t fallible = fallible_t(); bool memoryAllocationSuccess = true; if (!mDecodeJob.mChannelBuffers.SetLength(channelCount)) { memoryAllocationSuccess = false; } else { for (uint32_t i = 0; i < channelCount; ++i) { mDecodeJob.mChannelBuffers[i] = new(fallible) float[resampledFrames]; if (!mDecodeJob.mChannelBuffers[i]) { memoryAllocationSuccess = false; break; } } } if (!memoryAllocationSuccess) { ReportFailureOnMainThread(WebAudioDecodeJob::UnknownError); return; } nsAutoPtr<AudioData> audioData; while ((audioData = audioQueue.PopFront())) { audioData->EnsureAudioBuffer(); // could lead to a copy :( AudioDataValue* bufferData = static_cast<AudioDataValue*> (audioData->mAudioBuffer->Data()); if (sampleRate != destSampleRate) { const uint32_t maxOutSamples = resampledFrames - mDecodeJob.mWriteIndex; for (uint32_t i = 0; i < audioData->mChannels; ++i) { uint32_t inSamples = audioData->mFrames; uint32_t outSamples = maxOutSamples; WebAudioUtils::SpeexResamplerProcess( resampler, i, &bufferData[i * audioData->mFrames], &inSamples, mDecodeJob.mChannelBuffers[i] + mDecodeJob.mWriteIndex, &outSamples); if (i == audioData->mChannels - 1) { mDecodeJob.mWriteIndex += outSamples; MOZ_ASSERT(mDecodeJob.mWriteIndex <= resampledFrames); MOZ_ASSERT(inSamples == audioData->mFrames); } } } else { for (uint32_t i = 0; i < audioData->mChannels; ++i) { ConvertAudioSamples(&bufferData[i * audioData->mFrames], mDecodeJob.mChannelBuffers[i] + mDecodeJob.mWriteIndex, audioData->mFrames); if (i == audioData->mChannels - 1) { mDecodeJob.mWriteIndex += audioData->mFrames; } } } } if (sampleRate != destSampleRate) { uint32_t inputLatency = speex_resampler_get_input_latency(resampler); const uint32_t maxOutSamples = resampledFrames - mDecodeJob.mWriteIndex; for (uint32_t i = 0; i < channelCount; ++i) { uint32_t inSamples = inputLatency; uint32_t outSamples = maxOutSamples; WebAudioUtils::SpeexResamplerProcess( resampler, i, (AudioDataValue*)nullptr, &inSamples, mDecodeJob.mChannelBuffers[i] + mDecodeJob.mWriteIndex, &outSamples); if (i == channelCount - 1) { mDecodeJob.mWriteIndex += outSamples; MOZ_ASSERT(mDecodeJob.mWriteIndex <= resampledFrames); MOZ_ASSERT(inSamples == inputLatency); } } } mPhase = PhaseEnum::AllocateBuffer; RunNextPhase(); }
void MediaDecodeTask::FinishDecode() { mDecoderReader->Shutdown(); uint32_t frameCount = mAudioQueue.FrameCount(); uint32_t channelCount = mMediaInfo.mAudio.mChannels; uint32_t sampleRate = mMediaInfo.mAudio.mRate; if (!frameCount || !channelCount || !sampleRate) { ReportFailureOnMainThread(WebAudioDecodeJob::InvalidContent); return; } const uint32_t destSampleRate = mDecodeJob.mContext->SampleRate(); AutoResampler resampler; uint32_t resampledFrames = frameCount; if (sampleRate != destSampleRate) { resampledFrames = static_cast<uint32_t>( static_cast<uint64_t>(destSampleRate) * static_cast<uint64_t>(frameCount) / static_cast<uint64_t>(sampleRate) ); resampler = speex_resampler_init(channelCount, sampleRate, destSampleRate, SPEEX_RESAMPLER_QUALITY_DEFAULT, nullptr); speex_resampler_skip_zeros(resampler); resampledFrames += speex_resampler_get_output_latency(resampler); } // Allocate the channel buffers. Note that if we end up resampling, we may // write fewer bytes than mResampledFrames to the output buffer, in which // case mWriteIndex will tell us how many valid samples we have. mDecodeJob.mBuffer = ThreadSharedFloatArrayBufferList:: Create(channelCount, resampledFrames, fallible); if (!mDecodeJob.mBuffer) { ReportFailureOnMainThread(WebAudioDecodeJob::UnknownError); return; } RefPtr<MediaData> mediaData; while ((mediaData = mAudioQueue.PopFront())) { RefPtr<AudioData> audioData = mediaData->As<AudioData>(); audioData->EnsureAudioBuffer(); // could lead to a copy :( AudioDataValue* bufferData = static_cast<AudioDataValue*> (audioData->mAudioBuffer->Data()); if (sampleRate != destSampleRate) { const uint32_t maxOutSamples = resampledFrames - mDecodeJob.mWriteIndex; for (uint32_t i = 0; i < audioData->mChannels; ++i) { uint32_t inSamples = audioData->mFrames; uint32_t outSamples = maxOutSamples; float* outData = mDecodeJob.mBuffer->GetDataForWrite(i) + mDecodeJob.mWriteIndex; WebAudioUtils::SpeexResamplerProcess( resampler, i, &bufferData[i * audioData->mFrames], &inSamples, outData, &outSamples); if (i == audioData->mChannels - 1) { mDecodeJob.mWriteIndex += outSamples; MOZ_ASSERT(mDecodeJob.mWriteIndex <= resampledFrames); MOZ_ASSERT(inSamples == audioData->mFrames); } } } else { for (uint32_t i = 0; i < audioData->mChannels; ++i) { float* outData = mDecodeJob.mBuffer->GetDataForWrite(i) + mDecodeJob.mWriteIndex; ConvertAudioSamples(&bufferData[i * audioData->mFrames], outData, audioData->mFrames); if (i == audioData->mChannels - 1) { mDecodeJob.mWriteIndex += audioData->mFrames; } } } } if (sampleRate != destSampleRate) { uint32_t inputLatency = speex_resampler_get_input_latency(resampler); const uint32_t maxOutSamples = resampledFrames - mDecodeJob.mWriteIndex; for (uint32_t i = 0; i < channelCount; ++i) { uint32_t inSamples = inputLatency; uint32_t outSamples = maxOutSamples; float* outData = mDecodeJob.mBuffer->GetDataForWrite(i) + mDecodeJob.mWriteIndex; WebAudioUtils::SpeexResamplerProcess( resampler, i, (AudioDataValue*)nullptr, &inSamples, outData, &outSamples); if (i == channelCount - 1) { mDecodeJob.mWriteIndex += outSamples; MOZ_ASSERT(mDecodeJob.mWriteIndex <= resampledFrames); MOZ_ASSERT(inSamples == inputLatency); } } } mPhase = PhaseEnum::AllocateBuffer; NS_DispatchToMainThread(this); }