Commit 962ca451 authored by Meredith Lane's avatar Meredith Lane Committed by Commit Bot

Revert "Use resampler for playback speeds close to 1.0"

This reverts commit ab98b39d.

Reason for revert: Consistently failing on MSan https://crbug.com/1018617

Original change's description:
> Use resampler for playback speeds close to 1.0
> 
> The WSOLA algorithm introduces noticeable audio artifacts when doing
> small adjustments to playback rate (e.g. 1.03 playback speed). The
> distortions can be described as warbling or transient stuttering.
> 
> This CL introduces fixes this issue by using resampling instead, for
> speeds close to 1.00. The resampled audio doesn't have any distortions,
> but its pitch is shifted proportionally to the playback changes. The
> maximal slowdown/speed up we use resampling for is 5-6%, which
> corresponds to shifting by 1 note tone. Beyond that, the WSOLA
> artifacts become tolerable, and the pitch shifting doesn't.
> 
> Bug: 920019
> Change-Id: Ia1f93dc6a5de8be14e054b9e165fb9508af491f1
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1875654
> Commit-Queue: Thomas Guilbert <tguilbert@chromium.org>
> Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#709679}

TBR=dalecurtis@chromium.org,tguilbert@chromium.org

# Not skipping CQ checks because original CL landed > 1 day ago.

Bug: 920019
Change-Id: I5c7f531c446ebb2af24d26698994a0747dd68aba
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1882255Reviewed-by: default avatarMeredith Lane <meredithl@chromium.org>
Commit-Queue: Meredith Lane <meredithl@chromium.org>
Cr-Commit-Position: refs/heads/master@{#709788}
parent 7d288db7
...@@ -7,13 +7,11 @@ ...@@ -7,13 +7,11 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include "base/bind.h"
#include "base/logging.h" #include "base/logging.h"
#include "cc/base/math_util.h" #include "cc/base/math_util.h"
#include "media/base/audio_bus.h" #include "media/base/audio_bus.h"
#include "media/base/audio_timestamp_helper.h" #include "media/base/audio_timestamp_helper.h"
#include "media/base/limits.h" #include "media/base/limits.h"
#include "media/base/multi_channel_resampler.h"
#include "media/filters/wsola_internals.h" #include "media/filters/wsola_internals.h"
namespace media { namespace media {
...@@ -159,73 +157,6 @@ void AudioRendererAlgorithm::SetChannelMask(std::vector<bool> channel_mask) { ...@@ -159,73 +157,6 @@ void AudioRendererAlgorithm::SetChannelMask(std::vector<bool> channel_mask) {
CreateSearchWrappers(); CreateSearchWrappers();
} }
void AudioRendererAlgorithm::OnResamplerRead(int frame_delay,
AudioBus* audio_bus) {
input_frames_read_or_buffered_ +=
audio_buffer_.ReadFrames(audio_bus->frames(), 0, audio_bus);
}
int AudioRendererAlgorithm::CalculateOutputFramesResampled(
double playback_rate,
int requested_frames) {
double input_frames_consumed =
input_frames_read_or_buffered_ - resampler_->BufferedFrames();
int output_frames =
static_cast<int>(input_frames_consumed / playback_rate + 0.5);
// The first or second call to resample appears to consume more input frames
// than it actually does. This is due to the internals of |resampler_|
// treating the first data read differently, to prime internal buffers. We
// therefore appear to have read up to SincResampler::kKernelSize more frames
// when using the difference between calls to |resampler_->BufferedFrames()|.
// |resampler_| never actually writes more frames than we request out of it,
// so we can safely cap this value here.
return std::min(output_frames, requested_frames);
}
int AudioRendererAlgorithm::ResampleAndFill(AudioBus* dest,
int dest_offset,
int requested_frames,
double playback_rate) {
if (!resampler_) {
resampler_ = std::make_unique<MultiChannelResampler>(
channels_, playback_rate, SincResampler::kDefaultRequestSize,
base::BindRepeating(&AudioRendererAlgorithm::OnResamplerRead,
base::Unretained(this)));
}
resampler_->SetRatio(playback_rate);
// Reset with leftover frames from previous resampling iteration.
input_frames_read_or_buffered_ = resampler_->BufferedFrames();
// Directly use |dest| for the most common case of having 0 offset.
if (!dest_offset) {
resampler_->Resample(requested_frames, dest);
return CalculateOutputFramesResampled(playback_rate, requested_frames);
}
// This is only really used once, at the beginning of a stream, which means
// we can use a temporary variable, rather than saving it as a member.
// NOTE: We don't wrap |dest|'s channel data in an AudioBus wrapper, because
// |dest_offset| isn't aligned always with AudioBus::kChannelAlignment.
std::unique_ptr<AudioBus> resampler_output =
AudioBus::Create(channels_, requested_frames);
resampler_->Resample(requested_frames, resampler_output.get());
int output_frames =
CalculateOutputFramesResampled(playback_rate, requested_frames);
DCHECK_LE(output_frames, resampler_output->frames());
resampler_output->CopyPartialFramesTo(0, output_frames, dest_offset, dest);
return output_frames;
}
int AudioRendererAlgorithm::FillBuffer(AudioBus* dest, int AudioRendererAlgorithm::FillBuffer(AudioBus* dest,
int dest_offset, int dest_offset,
int requested_frames, int requested_frames,
...@@ -254,18 +185,6 @@ int AudioRendererAlgorithm::FillBuffer(AudioBus* dest, ...@@ -254,18 +185,6 @@ int AudioRendererAlgorithm::FillBuffer(AudioBus* dest,
return frames_read; return frames_read;
} }
// WSOLA at playback rates that are close to 1.0 produces noticeable
// warbling and stuttering. We prefer resampling the audio at these speeds.
// This does results in a noticeable pitch shift.
// NOTE: The cutoff values are arbitrary, and picked based off of a tradeoff
// between "resample pitch shift" vs "WSOLA distortions".
constexpr double kLowerResampleThreshold = 0.95;
constexpr double kUpperResampleThreshold = 1.06;
if (kLowerResampleThreshold <= playback_rate &&
playback_rate <= kUpperResampleThreshold) {
return ResampleAndFill(dest, dest_offset, requested_frames, playback_rate);
}
// Allocate structures on first non-1.0 playback rate; these can eat a fair // Allocate structures on first non-1.0 playback rate; these can eat a fair
// chunk of memory. ~56kB for stereo 48kHz, up to ~765kB for 7.1 192kHz. // chunk of memory. ~56kB for stereo 48kHz, up to ~765kB for 7.1 192kHz.
if (!ola_window_) { if (!ola_window_) {
...@@ -316,8 +235,6 @@ void AudioRendererAlgorithm::FlushBuffers() { ...@@ -316,8 +235,6 @@ void AudioRendererAlgorithm::FlushBuffers() {
wsola_output_->Zero(); wsola_output_->Zero();
num_complete_frames_ = 0; num_complete_frames_ = 0;
resampler_.reset();
// Reset |capacity_| so growth triggered by underflows doesn't penalize seek // Reset |capacity_| so growth triggered by underflows doesn't penalize seek
// time. // time.
capacity_ = initial_capacity_; capacity_ = initial_capacity_;
......
...@@ -35,7 +35,6 @@ ...@@ -35,7 +35,6 @@
namespace media { namespace media {
class AudioBus; class AudioBus;
class MultiChannelResampler;
class MEDIA_EXPORT AudioRendererAlgorithm { class MEDIA_EXPORT AudioRendererAlgorithm {
public: public:
...@@ -146,24 +145,6 @@ class MEDIA_EXPORT AudioRendererAlgorithm { ...@@ -146,24 +145,6 @@ class MEDIA_EXPORT AudioRendererAlgorithm {
// mask has been specified. // mask has been specified.
void CreateSearchWrappers(); void CreateSearchWrappers();
// Uses |resampler_| to speed up or slowdown audio, by using a resampling
// ratio of |playback_rate|.
int ResampleAndFill(AudioBus* dest,
int dest_offset,
int requested_frames,
double playback_rate);
// Called by |resampler_| to get more audio data.
void OnResamplerRead(int frame_delay, AudioBus* audio_bus);
// Calculate how many frames |resampler_| wrote to output, based off of
// |input_frames_read_or_buffered_| and |resampler_->BufferedFrames()|.
//
// NOTE: The return value is always <= |request_frames|. See comment in the
// implementation file.
int CalculateOutputFramesResampled(double playback_rate,
int requested_frames);
// Parameters. // Parameters.
AudioRendererAlgorithmParameters audio_renderer_algorithm_params_; AudioRendererAlgorithmParameters audio_renderer_algorithm_params_;
...@@ -217,14 +198,6 @@ class MEDIA_EXPORT AudioRendererAlgorithm { ...@@ -217,14 +198,6 @@ class MEDIA_EXPORT AudioRendererAlgorithm {
// specifies the index where the next WSOLA window has to overlap-and-add. // specifies the index where the next WSOLA window has to overlap-and-add.
int num_complete_frames_; int num_complete_frames_;
// Used to replace WSOLA algorithm at playback speeds close to 1.0. This is to
// prevent noticeable audio artifacts introduced by WSOLA, at the expense of
// changing the pitch of the audio.
std::unique_ptr<MultiChannelResampler> resampler_;
// Number of input frames read or buffered by |resampler_|.
double input_frames_read_or_buffered_ = 0;
// This stores a part of the output that is created but couldn't be rendered. // This stores a part of the output that is created but couldn't be rendered.
// Output is generated frame-by-frame which at some point might exceed the // Output is generated frame-by-frame which at some point might exceed the
// number of requested samples. Furthermore, due to overlap-and-add, // number of requested samples. Furthermore, due to overlap-and-add,
......
...@@ -189,14 +189,13 @@ class AudioRendererAlgorithmTest : public testing::Test { ...@@ -189,14 +189,13 @@ class AudioRendererAlgorithmTest : public testing::Test {
const int kDefaultFramesRequested = kOutputDurationInSec * const int kDefaultFramesRequested = kOutputDurationInSec *
algorithm_.samples_per_second(); algorithm_.samples_per_second();
TestPlaybackRate(playback_rate, kDefaultBufferSize, kDefaultFramesRequested, TestPlaybackRate(
0); playback_rate, kDefaultBufferSize, kDefaultFramesRequested);
} }
void TestPlaybackRate(double playback_rate, void TestPlaybackRate(double playback_rate,
int buffer_size_in_frames, int buffer_size_in_frames,
int total_frames_requested, int total_frames_requested) {
int dest_offset) {
int initial_frames_enqueued = frames_enqueued_; int initial_frames_enqueued = frames_enqueued_;
int initial_frames_buffered = algorithm_.frames_buffered(); int initial_frames_buffered = algorithm_.frames_buffered();
...@@ -212,10 +211,9 @@ class AudioRendererAlgorithmTest : public testing::Test { ...@@ -212,10 +211,9 @@ class AudioRendererAlgorithmTest : public testing::Test {
int frames_remaining = total_frames_requested; int frames_remaining = total_frames_requested;
bool first_fill_buffer = true; bool first_fill_buffer = true;
while (frames_remaining > 0) { while (frames_remaining > 0) {
int frames_requested = int frames_requested = std::min(buffer_size_in_frames, frames_remaining);
std::min(buffer_size_in_frames - dest_offset, frames_remaining); int frames_written =
int frames_written = algorithm_.FillBuffer( algorithm_.FillBuffer(bus.get(), 0, frames_requested, playback_rate);
bus.get(), dest_offset, frames_requested, playback_rate);
ASSERT_GT(frames_written, 0) << "Requested: " << frames_requested ASSERT_GT(frames_written, 0) << "Requested: " << frames_requested
<< ", playing at " << playback_rate; << ", playing at " << playback_rate;
...@@ -358,7 +356,7 @@ TEST_F(AudioRendererAlgorithmTest, InitializeWithLargeParameters) { ...@@ -358,7 +356,7 @@ TEST_F(AudioRendererAlgorithmTest, InitializeWithLargeParameters) {
TEST_F(AudioRendererAlgorithmTest, FillBuffer_Bitstream) { TEST_F(AudioRendererAlgorithmTest, FillBuffer_Bitstream) {
Initialize(CHANNEL_LAYOUT_STEREO, kSampleFormatEac3, kSamplesPerSecond, Initialize(CHANNEL_LAYOUT_STEREO, kSampleFormatEac3, kSamplesPerSecond,
kSamplesPerSecond / 100); kSamplesPerSecond / 100);
TestPlaybackRate(1.0, kFrameSize, 16 * kFrameSize, /* dest_offset */ 0); TestPlaybackRate(1.0, kFrameSize, 16 * kFrameSize);
} }
TEST_F(AudioRendererAlgorithmTest, FillBuffer_NormalRate) { TEST_F(AudioRendererAlgorithmTest, FillBuffer_NormalRate) {
...@@ -376,36 +374,6 @@ TEST_F(AudioRendererAlgorithmTest, FillBuffer_NearlyNormalSlowerRate) { ...@@ -376,36 +374,6 @@ TEST_F(AudioRendererAlgorithmTest, FillBuffer_NearlyNormalSlowerRate) {
TestPlaybackRate(0.9999); TestPlaybackRate(0.9999);
} }
// This test verifies that the resampling based time stretch algorithms works.
// The range of playback rates in which we use resampling is [0.95, 1.06].
TEST_F(AudioRendererAlgorithmTest, FillBuffer_ResamplingRates) {
Initialize();
TestPlaybackRate(0.94); // WSOLA.
TestPlaybackRate(0.95); // Lower limit of resampling.
TestPlaybackRate(0.97);
TestPlaybackRate(1.00);
TestPlaybackRate(1.04);
TestPlaybackRate(1.06); // Upper limit of resampling.
TestPlaybackRate(1.07); // WSOLA.
}
TEST_F(AudioRendererAlgorithmTest, FillBuffer_WithOffset) {
Initialize();
const int kBufferSize = algorithm_.samples_per_second() / 10;
const int kOffset = kBufferSize / 10;
const int kFramesRequested =
kOutputDurationInSec * algorithm_.samples_per_second();
// No time-strech.
TestPlaybackRate(1.00, kBufferSize, kFramesRequested, kOffset);
// Resampling based time-strech.
TestPlaybackRate(1.05, kBufferSize, kFramesRequested, kOffset);
// WSOLA based time-strech.
TestPlaybackRate(1.25, kBufferSize, kFramesRequested, kOffset);
}
TEST_F(AudioRendererAlgorithmTest, FillBuffer_OneAndAQuarterRate) { TEST_F(AudioRendererAlgorithmTest, FillBuffer_OneAndAQuarterRate) {
Initialize(); Initialize();
TestPlaybackRate(1.25); TestPlaybackRate(1.25);
...@@ -479,9 +447,9 @@ TEST_F(AudioRendererAlgorithmTest, FillBuffer_SmallBufferSize) { ...@@ -479,9 +447,9 @@ TEST_F(AudioRendererAlgorithmTest, FillBuffer_SmallBufferSize) {
Initialize(); Initialize();
static const int kBufferSizeInFrames = 1; static const int kBufferSizeInFrames = 1;
static const int kFramesRequested = kOutputDurationInSec * kSamplesPerSecond; static const int kFramesRequested = kOutputDurationInSec * kSamplesPerSecond;
TestPlaybackRate(1.0, kBufferSizeInFrames, kFramesRequested, 0); TestPlaybackRate(1.0, kBufferSizeInFrames, kFramesRequested);
TestPlaybackRate(0.5, kBufferSizeInFrames, kFramesRequested, 0); TestPlaybackRate(0.5, kBufferSizeInFrames, kFramesRequested);
TestPlaybackRate(1.5, kBufferSizeInFrames, kFramesRequested, 0); TestPlaybackRate(1.5, kBufferSizeInFrames, kFramesRequested);
} }
TEST_F(AudioRendererAlgorithmTest, FillBuffer_LargeBufferSize) { TEST_F(AudioRendererAlgorithmTest, FillBuffer_LargeBufferSize) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment