Ongoing fixes and improvements, transient suppressor is gone. Also, dropping isac because it doesn't seem to be useful, and is just build system deadweight now. Upstream references: Version: 131.0.6778.200 WebRTC: 79aff54b0fa9238ce3518dd9eaf9610cd6f22e82 Chromium: 2a19506ad24af755f2a215a4c61f775393e0db42
217 lines
7.1 KiB
C++
217 lines
7.1 KiB
C++
/*
|
|
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#include "audio/utility/audio_frame_operations.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <utility>
|
|
|
|
#include "common_audio/include/audio_util.h"
|
|
#include "rtc_base/checks.h"
|
|
#include "rtc_base/numerics/safe_conversions.h"
|
|
|
|
namespace webrtc {
|
|
namespace {
|
|
|
|
// 2.7ms @ 48kHz, 4ms @ 32kHz, 8ms @ 16kHz.
|
|
const size_t kMuteFadeFrames = 128;
|
|
const float kMuteFadeInc = 1.0f / kMuteFadeFrames;
|
|
|
|
} // namespace
|
|
|
|
void AudioFrameOperations::QuadToStereo(
|
|
InterleavedView<const int16_t> src_audio,
|
|
InterleavedView<int16_t> dst_audio) {
|
|
RTC_DCHECK_EQ(NumChannels(src_audio), 4);
|
|
RTC_DCHECK_EQ(NumChannels(dst_audio), 2);
|
|
RTC_DCHECK_EQ(SamplesPerChannel(src_audio), SamplesPerChannel(dst_audio));
|
|
for (size_t i = 0; i < SamplesPerChannel(src_audio); ++i) {
|
|
auto dst_frame = i * 2;
|
|
dst_audio[dst_frame] =
|
|
(static_cast<int32_t>(src_audio[4 * i]) + src_audio[4 * i + 1]) >> 1;
|
|
dst_audio[dst_frame + 1] =
|
|
(static_cast<int32_t>(src_audio[4 * i + 2]) + src_audio[4 * i + 3]) >>
|
|
1;
|
|
}
|
|
}
|
|
|
|
int AudioFrameOperations::QuadToStereo(AudioFrame* frame) {
|
|
if (frame->num_channels_ != 4) {
|
|
return -1;
|
|
}
|
|
|
|
RTC_DCHECK_LE(frame->samples_per_channel_ * 4,
|
|
AudioFrame::kMaxDataSizeSamples);
|
|
|
|
if (!frame->muted()) {
|
|
// Note that `src` and `dst` will map in to the same buffer, but the call
|
|
// to `mutable_data()` changes the layout of `frame`, so `src` and `dst`
|
|
// will have different dimensions (important to call `data_view()` first).
|
|
auto src = frame->data_view();
|
|
auto dst = frame->mutable_data(frame->samples_per_channel_, 2);
|
|
QuadToStereo(src, dst);
|
|
} else {
|
|
frame->num_channels_ = 2;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void AudioFrameOperations::DownmixChannels(
|
|
InterleavedView<const int16_t> src_audio,
|
|
InterleavedView<int16_t> dst_audio) {
|
|
RTC_DCHECK_EQ(SamplesPerChannel(src_audio), SamplesPerChannel(dst_audio));
|
|
if (NumChannels(src_audio) > 1 && IsMono(dst_audio)) {
|
|
// TODO(tommi): change DownmixInterleavedToMono to support InterleavedView
|
|
// and MonoView.
|
|
DownmixInterleavedToMono(&src_audio.data()[0], SamplesPerChannel(src_audio),
|
|
NumChannels(src_audio), &dst_audio.data()[0]);
|
|
} else if (NumChannels(src_audio) == 4 && NumChannels(dst_audio) == 2) {
|
|
QuadToStereo(src_audio, dst_audio);
|
|
} else {
|
|
RTC_DCHECK_NOTREACHED() << "src_channels: " << NumChannels(src_audio)
|
|
<< ", dst_channels: " << NumChannels(dst_audio);
|
|
}
|
|
}
|
|
|
|
void AudioFrameOperations::DownmixChannels(size_t dst_channels,
|
|
AudioFrame* frame) {
|
|
RTC_DCHECK_LE(frame->samples_per_channel_ * frame->num_channels_,
|
|
AudioFrame::kMaxDataSizeSamples);
|
|
if (frame->num_channels_ > 1 && dst_channels == 1) {
|
|
if (!frame->muted()) {
|
|
DownmixInterleavedToMono(frame->data(), frame->samples_per_channel_,
|
|
frame->num_channels_, frame->mutable_data());
|
|
}
|
|
frame->num_channels_ = 1;
|
|
} else if (frame->num_channels_ == 4 && dst_channels == 2) {
|
|
int err = QuadToStereo(frame);
|
|
RTC_DCHECK_EQ(err, 0);
|
|
} else {
|
|
RTC_DCHECK_NOTREACHED() << "src_channels: " << frame->num_channels_
|
|
<< ", dst_channels: " << dst_channels;
|
|
}
|
|
}
|
|
|
|
void AudioFrameOperations::UpmixChannels(size_t target_number_of_channels,
|
|
AudioFrame* frame) {
|
|
RTC_DCHECK_EQ(frame->num_channels_, 1);
|
|
RTC_DCHECK_LE(frame->samples_per_channel_ * target_number_of_channels,
|
|
AudioFrame::kMaxDataSizeSamples);
|
|
|
|
if (frame->num_channels_ != 1 ||
|
|
frame->samples_per_channel_ * target_number_of_channels >
|
|
AudioFrame::kMaxDataSizeSamples) {
|
|
return;
|
|
}
|
|
|
|
if (!frame->muted()) {
|
|
// Up-mixing done in place. Going backwards through the frame ensure nothing
|
|
// is irrevocably overwritten.
|
|
auto frame_data = frame->mutable_data(frame->samples_per_channel_,
|
|
target_number_of_channels);
|
|
for (int i = frame->samples_per_channel_ - 1; i >= 0; --i) {
|
|
for (size_t j = 0; j < target_number_of_channels; ++j) {
|
|
frame_data[target_number_of_channels * i + j] = frame_data[i];
|
|
}
|
|
}
|
|
} else {
|
|
frame->num_channels_ = target_number_of_channels;
|
|
}
|
|
}
|
|
|
|
void AudioFrameOperations::SwapStereoChannels(AudioFrame* frame) {
|
|
RTC_DCHECK(frame);
|
|
if (frame->num_channels_ != 2 || frame->muted()) {
|
|
return;
|
|
}
|
|
|
|
int16_t* frame_data = frame->mutable_data();
|
|
for (size_t i = 0; i < frame->samples_per_channel_ * 2; i += 2) {
|
|
std::swap(frame_data[i], frame_data[i + 1]);
|
|
}
|
|
}
|
|
|
|
void AudioFrameOperations::Mute(AudioFrame* frame,
|
|
bool previous_frame_muted,
|
|
bool current_frame_muted) {
|
|
RTC_DCHECK(frame);
|
|
if (!previous_frame_muted && !current_frame_muted) {
|
|
// Not muted, don't touch.
|
|
} else if (previous_frame_muted && current_frame_muted) {
|
|
// Frame fully muted.
|
|
size_t total_samples = frame->samples_per_channel_ * frame->num_channels_;
|
|
RTC_DCHECK_GE(AudioFrame::kMaxDataSizeSamples, total_samples);
|
|
frame->Mute();
|
|
} else {
|
|
// Fade is a no-op on a muted frame.
|
|
if (frame->muted()) {
|
|
return;
|
|
}
|
|
|
|
// Limit number of samples to fade, if frame isn't long enough.
|
|
size_t count = kMuteFadeFrames;
|
|
float inc = kMuteFadeInc;
|
|
if (frame->samples_per_channel_ < kMuteFadeFrames) {
|
|
count = frame->samples_per_channel_;
|
|
if (count > 0) {
|
|
inc = 1.0f / count;
|
|
}
|
|
}
|
|
|
|
size_t start = 0;
|
|
size_t end = count;
|
|
float start_g = 0.0f;
|
|
if (current_frame_muted) {
|
|
// Fade out the last `count` samples of frame.
|
|
RTC_DCHECK(!previous_frame_muted);
|
|
start = frame->samples_per_channel_ - count;
|
|
end = frame->samples_per_channel_;
|
|
start_g = 1.0f;
|
|
inc = -inc;
|
|
} else {
|
|
// Fade in the first `count` samples of frame.
|
|
RTC_DCHECK(previous_frame_muted);
|
|
}
|
|
|
|
// Perform fade.
|
|
int16_t* frame_data = frame->mutable_data();
|
|
size_t channels = frame->num_channels_;
|
|
for (size_t j = 0; j < channels; ++j) {
|
|
float g = start_g;
|
|
for (size_t i = start * channels; i < end * channels; i += channels) {
|
|
g += inc;
|
|
frame_data[i + j] *= g;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AudioFrameOperations::Mute(AudioFrame* frame) {
|
|
Mute(frame, true, true);
|
|
}
|
|
|
|
int AudioFrameOperations::ScaleWithSat(float scale, AudioFrame* frame) {
|
|
if (frame->muted()) {
|
|
return 0;
|
|
}
|
|
|
|
int16_t* frame_data = frame->mutable_data();
|
|
for (size_t i = 0; i < frame->samples_per_channel_ * frame->num_channels_;
|
|
i++) {
|
|
frame_data[i] = rtc::saturated_cast<int16_t>(scale * frame_data[i]);
|
|
}
|
|
return 0;
|
|
}
|
|
} // namespace webrtc
|