/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
bool AudioConverter::CanConvert(const AudioConfig& aIn, const AudioConfig& aOut) { if (aIn.Format() != aOut.Format() ||
aIn.Interleaved() != aOut.Interleaved()) {
NS_WARNING("No format conversion is supported at this stage"); returnfalse;
} if (aIn.Channels() != aOut.Channels() && aOut.Channels() > 2) {
NS_WARNING( "Only down/upmixing to mono or stereo is supported at this stage"); returnfalse;
} if (!aOut.Interleaved()) {
NS_WARNING("planar audio format not supported"); returnfalse;
} returntrue;
}
bool AudioConverter::CanWorkInPlace() const { bool needDownmix = mIn.Channels() > mOut.Channels(); bool needUpmix = mIn.Channels() < mOut.Channels(); bool canDownmixInPlace =
mIn.Channels() * AudioConfig::SampleSize(mIn.Format()) >=
mOut.Channels() * AudioConfig::SampleSize(mOut.Format()); bool needResample = mIn.Rate() != mOut.Rate(); bool canResampleInPlace = mIn.Rate() >= mOut.Rate(); // We should be able to work in place if 1s of audio input takes less space // than 1s of audio output. However, as we downmix before resampling we can't // perform any upsampling in place (e.g. if incoming rate >= outgoing rate) return !needUpmix && (!needDownmix || canDownmixInPlace) &&
(!needResample || canResampleInPlace);
}
if (mChannelOrderMap.IsEmpty() || mOut.Channels() == 1 ||
mOut.Layout() == mIn.Layout()) { // If channel count is 1, planar and non-planar formats are the same or // there's nothing to reorder, or if we don't know how to re-order. if (aOut != aIn) {
memmove(aOut, aIn, FramesOutToBytes(aFrames));
} return;
}
int error; if (mOut.Format() == AudioConfig::FORMAT_FLT) { constfloat* in = reinterpret_cast<constfloat*>(aIn); float* out = reinterpret_cast<float*>(aOut);
error = speex_resampler_process_interleaved_float(mResampler, in, &inframes,
out, &outframes);
} elseif (mOut.Format() == AudioConfig::FORMAT_S16) { const int16_t* in = reinterpret_cast<const int16_t*>(aIn);
int16_t* out = reinterpret_cast<int16_t*>(aOut);
error = speex_resampler_process_interleaved_int(mResampler, in, &inframes,
out, &outframes);
} else {
MOZ_DIAGNOSTIC_CRASH("Unsupported data type");
error = RESAMPLER_ERR_ALLOC_FAILED;
}
MOZ_ASSERT(error == RESAMPLER_ERR_SUCCESS); if (error != RESAMPLER_ERR_SUCCESS) {
speex_resampler_destroy(mResampler);
mResampler = nullptr; return 0;
}
MOZ_ASSERT(inframes == aFrames, "Some frames will be dropped"); return outframes;
}
void AudioConverter::RecreateResampler() { if (mResampler) {
speex_resampler_destroy(mResampler);
} int error;
mResampler = speex_resampler_init(mOut.Channels(), mIn.Rate(), mOut.Rate(),
SPEEX_RESAMPLER_QUALITY_DEFAULT, &error);
if (error == RESAMPLER_ERR_SUCCESS) {
speex_resampler_skip_zeros(mResampler);
} else {
NS_WARNING("Failed to initialize resampler.");
mResampler = nullptr;
}
}
size_t AudioConverter::DrainResampler(void* aOut) { if (!mResampler) { return 0;
} int frames = speex_resampler_get_input_latency(mResampler);
AlignedByteBuffer buffer(FramesOutToBytes(frames)); if (!buffer) { // OOM return 0;
}
frames = ResampleAudio(aOut, buffer.Data(), frames); // Tore down the resampler as it's easier than handling follow-up.
RecreateResampler(); return frames;
}
size_t AudioConverter::UpmixAudio(void* aOut, constvoid* aIn,
size_t aFrames) const {
MOZ_ASSERT(mIn.Format() == AudioConfig::FORMAT_S16 ||
mIn.Format() == AudioConfig::FORMAT_FLT);
MOZ_ASSERT(mIn.Channels() < mOut.Channels());
MOZ_ASSERT(mIn.Channels() == 1, "Can only upmix mono for now");
MOZ_ASSERT(mOut.Channels() == 2, "Can only upmix to stereo for now");
if (!mIn.Layout().IsValid() || !mOut.Layout().IsValid() ||
mOut.Channels() != 2) { // Dumb copy the channels and insert silence for the extra channels. if (mIn.Format() == AudioConfig::FORMAT_FLT) {
dumbUpDownMix(static_cast<float*>(aOut), mOut.Channels(), static_cast<constfloat*>(aIn), mIn.Channels(), aFrames);
} elseif (mIn.Format() == AudioConfig::FORMAT_S16) {
dumbUpDownMix(static_cast<int16_t*>(aOut), mOut.Channels(), static_cast<const int16_t*>(aIn), mIn.Channels(), aFrames);
} else {
MOZ_DIAGNOSTIC_CRASH("Unsupported data type");
} return aFrames;
}
// Upmix mono to stereo. // This is a very dumb mono to stereo upmixing, power levels are preserved // following the calculation: left = right = -3dB*mono. if (mIn.Format() == AudioConfig::FORMAT_FLT) { constfloat m3db = std::sqrt(0.5); // -3dB = sqrt(1/2) constfloat* in = static_cast<constfloat*>(aIn); float* out = static_cast<float*>(aOut); for (size_t fIdx = 0; fIdx < aFrames; ++fIdx) { float sample = in[fIdx] * m3db; // The samples of the buffer would be interleaved.
*out++ = sample;
*out++ = sample;
}
} elseif (mIn.Format() == AudioConfig::FORMAT_S16) { const int16_t* in = static_cast<const int16_t*>(aIn);
int16_t* out = static_cast<int16_t*>(aOut); for (size_t fIdx = 0; fIdx < aFrames; ++fIdx) {
int16_t sample =
((int32_t)in[fIdx] * 11585) >> 14; // close enough to i*sqrt(0.5) // The samples of the buffer would be interleaved.
*out++ = sample;
*out++ = sample;
}
} else {
MOZ_DIAGNOSTIC_CRASH("Unsupported data type");
}
return aFrames;
}
size_t AudioConverter::ResampleRecipientFrames(size_t aFrames) const { if (!aFrames && mIn.Rate() != mOut.Rate()) { if (!mResampler) { return 0;
} // We drain by pushing in get_input_latency() samples of 0
aFrames = speex_resampler_get_input_latency(mResampler);
} return (uint64_t)aFrames * mOut.Rate() / mIn.Rate() + 1;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.