Alex Loiko | 99a2c5d | 2018-02-27 14:09:47 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license |
| 5 | * that can be found in the LICENSE file in the root of the source |
| 6 | * tree. An additional intellectual property rights grant can be found |
| 7 | * in the file PATENTS. All contributing project authors may |
| 8 | * be found in the AUTHORS file in the root of the source tree. |
| 9 | */ |
| 10 | |
Jonas Olsson | a4d8737 | 2019-07-05 19:08:33 +0200 | [diff] [blame] | 11 | #include "api/audio/audio_mixer.h" |
| 12 | |
Alex Loiko | 99a2c5d | 2018-02-27 14:09:47 +0100 | [diff] [blame] | 13 | #include <cstring> |
| 14 | #include <iostream> |
| 15 | #include <vector> |
| 16 | |
Mirko Bonadei | 2ab97f6 | 2019-07-18 13:44:12 +0200 | [diff] [blame] | 17 | #include "absl/flags/flag.h" |
| 18 | #include "absl/flags/parse.h" |
Alex Loiko | 99a2c5d | 2018-02-27 14:09:47 +0100 | [diff] [blame] | 19 | #include "common_audio/wav_file.h" |
| 20 | #include "modules/audio_mixer/audio_mixer_impl.h" |
| 21 | #include "modules/audio_mixer/default_output_rate_calculator.h" |
Jonas Olsson | 366a50c | 2018-09-06 13:41:30 +0200 | [diff] [blame] | 22 | #include "rtc_base/strings/string_builder.h" |
Alex Loiko | 99a2c5d | 2018-02-27 14:09:47 +0100 | [diff] [blame] | 23 | |
Mirko Bonadei | 2ab97f6 | 2019-07-18 13:44:12 +0200 | [diff] [blame] | 24 | ABSL_FLAG(int, |
| 25 | sampling_rate, |
| 26 | 16000, |
| 27 | "Rate at which to mix (all input streams must have this rate)"); |
Alex Loiko | 99a2c5d | 2018-02-27 14:09:47 +0100 | [diff] [blame] | 28 | |
Mirko Bonadei | 2ab97f6 | 2019-07-18 13:44:12 +0200 | [diff] [blame] | 29 | ABSL_FLAG(bool, |
| 30 | stereo, |
| 31 | false, |
| 32 | "Enable stereo (interleaved). Inputs need not be as this parameter."); |
Alex Loiko | 99a2c5d | 2018-02-27 14:09:47 +0100 | [diff] [blame] | 33 | |
Mirko Bonadei | 2ab97f6 | 2019-07-18 13:44:12 +0200 | [diff] [blame] | 34 | ABSL_FLAG(bool, limiter, true, "Enable limiter."); |
| 35 | ABSL_FLAG(std::string, |
| 36 | output_file, |
| 37 | "mixed_file.wav", |
| 38 | "File in which to store the mixed result."); |
| 39 | ABSL_FLAG(std::string, input_file_1, "", "First input. Default none."); |
| 40 | ABSL_FLAG(std::string, input_file_2, "", "Second input. Default none."); |
| 41 | ABSL_FLAG(std::string, input_file_3, "", "Third input. Default none."); |
| 42 | ABSL_FLAG(std::string, input_file_4, "", "Fourth input. Default none."); |
Alex Loiko | 99a2c5d | 2018-02-27 14:09:47 +0100 | [diff] [blame] | 43 | |
| 44 | namespace webrtc { |
| 45 | namespace test { |
| 46 | |
| 47 | class FilePlayingSource : public AudioMixer::Source { |
| 48 | public: |
| 49 | explicit FilePlayingSource(std::string filename) |
| 50 | : wav_reader_(new WavReader(filename)), |
| 51 | sample_rate_hz_(wav_reader_->sample_rate()), |
| 52 | samples_per_channel_(sample_rate_hz_ / 100), |
| 53 | number_of_channels_(wav_reader_->num_channels()) {} |
| 54 | |
| 55 | AudioFrameInfo GetAudioFrameWithInfo(int target_rate_hz, |
| 56 | AudioFrame* frame) override { |
| 57 | frame->samples_per_channel_ = samples_per_channel_; |
| 58 | frame->num_channels_ = number_of_channels_; |
| 59 | frame->sample_rate_hz_ = target_rate_hz; |
| 60 | |
| 61 | RTC_CHECK_EQ(target_rate_hz, sample_rate_hz_); |
| 62 | |
| 63 | const size_t num_to_read = number_of_channels_ * samples_per_channel_; |
| 64 | const size_t num_read = |
| 65 | wav_reader_->ReadSamples(num_to_read, frame->mutable_data()); |
| 66 | |
| 67 | file_has_ended_ = num_to_read != num_read; |
| 68 | if (file_has_ended_) { |
| 69 | frame->Mute(); |
| 70 | } |
| 71 | return file_has_ended_ ? AudioFrameInfo::kMuted : AudioFrameInfo::kNormal; |
| 72 | } |
| 73 | |
| 74 | int Ssrc() const override { return 0; } |
| 75 | |
| 76 | int PreferredSampleRate() const override { return sample_rate_hz_; } |
| 77 | |
| 78 | bool FileHasEnded() const { return file_has_ended_; } |
| 79 | |
| 80 | std::string ToString() const { |
Jonas Olsson | 366a50c | 2018-09-06 13:41:30 +0200 | [diff] [blame] | 81 | rtc::StringBuilder ss; |
Alex Loiko | 99a2c5d | 2018-02-27 14:09:47 +0100 | [diff] [blame] | 82 | ss << "{rate: " << sample_rate_hz_ << ", channels: " << number_of_channels_ |
| 83 | << ", samples_tot: " << wav_reader_->num_samples() << "}"; |
Jonas Olsson | 84df1c7 | 2018-09-14 16:59:32 +0200 | [diff] [blame] | 84 | return ss.Release(); |
Alex Loiko | 99a2c5d | 2018-02-27 14:09:47 +0100 | [diff] [blame] | 85 | } |
| 86 | |
| 87 | private: |
| 88 | std::unique_ptr<WavReader> wav_reader_; |
| 89 | int sample_rate_hz_; |
| 90 | int samples_per_channel_; |
| 91 | int number_of_channels_; |
| 92 | bool file_has_ended_ = false; |
| 93 | }; |
| 94 | } // namespace test |
| 95 | } // namespace webrtc |
| 96 | |
| 97 | namespace { |
| 98 | |
| 99 | const std::vector<std::string> parse_input_files() { |
| 100 | std::vector<std::string> result; |
Mirko Bonadei | 2ab97f6 | 2019-07-18 13:44:12 +0200 | [diff] [blame] | 101 | for (auto& x : |
| 102 | {absl::GetFlag(FLAGS_input_file_1), absl::GetFlag(FLAGS_input_file_2), |
| 103 | absl::GetFlag(FLAGS_input_file_3), absl::GetFlag(FLAGS_input_file_4)}) { |
| 104 | if (!x.empty()) { |
Alex Loiko | 99a2c5d | 2018-02-27 14:09:47 +0100 | [diff] [blame] | 105 | result.push_back(x); |
| 106 | } |
| 107 | } |
| 108 | return result; |
| 109 | } |
| 110 | } // namespace |
| 111 | |
| 112 | int main(int argc, char* argv[]) { |
Mirko Bonadei | 2ab97f6 | 2019-07-18 13:44:12 +0200 | [diff] [blame] | 113 | absl::ParseCommandLine(argc, argv); |
Alex Loiko | 99a2c5d | 2018-02-27 14:09:47 +0100 | [diff] [blame] | 114 | |
| 115 | rtc::scoped_refptr<webrtc::AudioMixerImpl> mixer( |
| 116 | webrtc::AudioMixerImpl::Create( |
| 117 | std::unique_ptr<webrtc::OutputRateCalculator>( |
| 118 | new webrtc::DefaultOutputRateCalculator()), |
Mirko Bonadei | 2ab97f6 | 2019-07-18 13:44:12 +0200 | [diff] [blame] | 119 | absl::GetFlag(FLAGS_limiter))); |
Alex Loiko | 99a2c5d | 2018-02-27 14:09:47 +0100 | [diff] [blame] | 120 | |
| 121 | const std::vector<std::string> input_files = parse_input_files(); |
| 122 | std::vector<webrtc::test::FilePlayingSource> sources; |
Mirko Bonadei | 2ab97f6 | 2019-07-18 13:44:12 +0200 | [diff] [blame] | 123 | const int num_channels = absl::GetFlag(FLAGS_stereo) ? 2 : 1; |
Mirko Bonadei | 649a4c2 | 2019-01-29 10:11:53 +0100 | [diff] [blame] | 124 | sources.reserve(input_files.size()); |
Mirko Bonadei | 739baf0 | 2019-01-27 17:29:42 +0100 | [diff] [blame] | 125 | for (const auto& input_file : input_files) { |
Alex Loiko | 99a2c5d | 2018-02-27 14:09:47 +0100 | [diff] [blame] | 126 | sources.emplace_back(input_file); |
| 127 | } |
| 128 | |
| 129 | for (auto& source : sources) { |
| 130 | auto error = mixer->AddSource(&source); |
| 131 | RTC_CHECK(error); |
| 132 | } |
| 133 | |
| 134 | if (sources.empty()) { |
| 135 | std::cout << "Need at least one source!\n"; |
Alex Loiko | 99a2c5d | 2018-02-27 14:09:47 +0100 | [diff] [blame] | 136 | return 1; |
| 137 | } |
| 138 | |
| 139 | const size_t sample_rate = sources[0].PreferredSampleRate(); |
| 140 | for (const auto& source : sources) { |
| 141 | RTC_CHECK_EQ(sample_rate, source.PreferredSampleRate()); |
| 142 | } |
| 143 | |
| 144 | // Print stats. |
Mirko Bonadei | 2ab97f6 | 2019-07-18 13:44:12 +0200 | [diff] [blame] | 145 | std::cout << "Limiting is: " << (absl::GetFlag(FLAGS_limiter) ? "on" : "off") |
| 146 | << "\n" |
Jonas Olsson | b2b2031 | 2020-01-14 12:11:31 +0100 | [diff] [blame] | 147 | "Channels: " |
| 148 | << num_channels |
| 149 | << "\n" |
| 150 | "Rate: " |
| 151 | << sample_rate |
| 152 | << "\n" |
| 153 | "Number of input streams: " |
| 154 | << input_files.size() << "\n"; |
Alex Loiko | 99a2c5d | 2018-02-27 14:09:47 +0100 | [diff] [blame] | 155 | for (const auto& source : sources) { |
| 156 | std::cout << "\t" << source.ToString() << "\n"; |
| 157 | } |
| 158 | std::cout << "Now mixing\n...\n"; |
| 159 | |
Mirko Bonadei | 2ab97f6 | 2019-07-18 13:44:12 +0200 | [diff] [blame] | 160 | webrtc::WavWriter wav_writer(absl::GetFlag(FLAGS_output_file), sample_rate, |
| 161 | num_channels); |
Alex Loiko | 99a2c5d | 2018-02-27 14:09:47 +0100 | [diff] [blame] | 162 | |
| 163 | webrtc::AudioFrame frame; |
| 164 | |
| 165 | bool all_streams_finished = false; |
| 166 | while (!all_streams_finished) { |
| 167 | mixer->Mix(num_channels, &frame); |
| 168 | RTC_CHECK_EQ(sample_rate / 100, frame.samples_per_channel_); |
| 169 | RTC_CHECK_EQ(sample_rate, frame.sample_rate_hz_); |
| 170 | RTC_CHECK_EQ(num_channels, frame.num_channels_); |
| 171 | wav_writer.WriteSamples(frame.data(), |
| 172 | num_channels * frame.samples_per_channel_); |
| 173 | |
| 174 | all_streams_finished = |
| 175 | std::all_of(sources.begin(), sources.end(), |
| 176 | [](const webrtc::test::FilePlayingSource& source) { |
| 177 | return source.FileHasEnded(); |
| 178 | }); |
| 179 | } |
| 180 | |
| 181 | std::cout << "Done!\n" << std::endl; |
| 182 | } |