| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <algorithm> |
| #include <cmath> |
| #include <memory> |
| #include <vector> |
| |
| #include "base/check_op.h" |
| #include "base/functional/bind.h" |
| #include "chromecast/media/api/cast_audio_resampler.h" |
| #include "media/base/sinc_resampler.h" |
| |
| namespace chromecast { |
| namespace media { |
| |
| namespace { |
| |
| constexpr int kRequestFrames = 128; |
| |
| class CastAudioResamplerImpl : public CastAudioResampler { |
| public: |
| CastAudioResamplerImpl(int channel_count, |
| int input_sample_rate, |
| int output_sample_rate) |
| : channel_count_(channel_count) { |
| DCHECK_GT(channel_count_, 0); |
| const double io_sample_rate_ratio = |
| static_cast<double>(input_sample_rate) / output_sample_rate; |
| resamplers_.reserve(channel_count_); |
| buffered_input_.channels.reserve(channel_count_); |
| for (int c = 0; c < channel_count_; ++c) { |
| resamplers_.push_back(std::make_unique<::media::SincResampler>( |
| io_sample_rate_ratio, kRequestFrames, |
| base::BindRepeating(&CastAudioResamplerImpl::ReadCallback, |
| base::Unretained(this), c))); |
| buffered_input_.channels.push_back( |
| std::make_unique<float[]>(kRequestFrames)); |
| } |
| } |
| |
| ~CastAudioResamplerImpl() override = default; |
| |
| CastAudioResamplerImpl(const CastAudioResamplerImpl&) = delete; |
| CastAudioResamplerImpl& operator=(const CastAudioResamplerImpl&) = delete; |
| |
| private: |
| void ResampleOneChunk(std::vector<float>* output_channels) { |
| int output_frame_offset = output_channels[0].size(); |
| int output_frames = resamplers_[0]->ChunkSize(); |
| for (int c = 0; c < channel_count_; ++c) { |
| output_channels[c].resize(output_frame_offset + output_frames); |
| resamplers_[c]->Resample(output_frames, |
| output_channels[c].data() + output_frame_offset); |
| } |
| } |
| |
| void ReadCallback(int channel_index, int frames, float* dest) { |
| DCHECK_LE(buffered_input_.frames, frames); |
| std::copy_n(buffered_input_.channels[channel_index].get(), |
| buffered_input_.frames, dest); |
| |
| int frames_left = frames - buffered_input_.frames; |
| int dest_offset = buffered_input_.frames; |
| if (frames_left) { |
| CopyCurrentInputTo(channel_index, frames_left, dest + dest_offset); |
| } |
| |
| if (channel_index == channel_count_ - 1) { |
| buffered_input_.frames = 0; |
| } |
| } |
| |
| void CopyCurrentInputTo(int channel_index, int frames_to_copy, float* dest) { |
| DCHECK(current_input_.data); |
| DCHECK_LE(current_input_.frame_offset + frames_to_copy, |
| current_input_.frames); |
| std::copy_n(current_input_.data + channel_index * current_input_.frames + |
| current_input_.frame_offset, |
| frames_to_copy, dest); |
| if (channel_index == channel_count_ - 1) { |
| current_input_.frame_offset += frames_to_copy; |
| } |
| } |
| |
| // CastAudioResampler implementation: |
| void Resample(const float* input, |
| int num_frames, |
| std::vector<float>* output_channels) override { |
| current_input_.data = input; |
| current_input_.frames = num_frames; |
| current_input_.frame_offset = 0; |
| |
| while (buffered_input_.frames + current_input_.frames - |
| current_input_.frame_offset >= |
| kRequestFrames) { |
| ResampleOneChunk(output_channels); |
| } |
| |
| int frames_left = current_input_.frames - current_input_.frame_offset; |
| DCHECK_LE(buffered_input_.frames + frames_left, kRequestFrames); |
| for (int c = 0; c < channel_count_; ++c) { |
| CopyCurrentInputTo( |
| c, frames_left, |
| buffered_input_.channels[c].get() + buffered_input_.frames); |
| } |
| buffered_input_.frames += frames_left; |
| |
| current_input_.data = nullptr; |
| current_input_.frames = 0; |
| current_input_.frame_offset = 0; |
| } |
| |
| void Flush(std::vector<float>* output_channels) override { |
| // TODO(kmackay) May need some additional flushing to get out data stored in |
| // the SincResamplers. |
| if (buffered_input_.frames == 0) { |
| return; |
| } |
| |
| for (int c = 0; c < channel_count_; ++c) { |
| std::fill_n(buffered_input_.channels[c].get() + buffered_input_.frames, |
| kRequestFrames - buffered_input_.frames, 0); |
| } |
| buffered_input_.frames = kRequestFrames; |
| while (buffered_input_.frames) { |
| ResampleOneChunk(output_channels); |
| } |
| |
| for (int c = 0; c < channel_count_; ++c) { |
| resamplers_[c]->Flush(); |
| } |
| } |
| |
| int BufferedInputFrames() const override { |
| return buffered_input_.frames + |
| std::round(resamplers_[0]->BufferedFrames()); |
| } |
| |
| const int channel_count_; |
| |
| std::vector<std::unique_ptr<::media::SincResampler>> resamplers_; |
| |
| struct InputBuffer { |
| std::vector<std::unique_ptr<float[]>> channels; |
| int frames = 0; |
| } buffered_input_; |
| |
| struct InputData { |
| const float* data = nullptr; |
| int frames = 0; |
| int frame_offset = 0; |
| } current_input_; |
| }; |
| |
| } // namespace |
| |
| // static |
| std::unique_ptr<CastAudioResampler> CastAudioResampler::Create( |
| int channel_count, |
| int input_sample_rate, |
| int output_sample_rate) { |
| return std::make_unique<CastAudioResamplerImpl>( |
| channel_count, input_sample_rate, output_sample_rate); |
| } |
| |
| } // namespace media |
| } // namespace chromecast |