| /* | |
| * 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 "modules/audio_coding/test/PCMFile.h" | |
| #include <cctype> | |
| #include <cstdint> | |
| #include <cstdio> | |
| #include <cstdlib> | |
| #include <cstring> | |
| #include <string> | |
| #include "absl/strings/string_view.h" | |
| #include "api/audio/audio_frame.h" | |
| #include "rtc_base/checks.h" | |
| #include "test/gtest.h" | |
| namespace webrtc { | |
| #define MAX_FILE_NAME_LENGTH_BYTE 500 | |
| PCMFile::PCMFile() | |
| : pcm_file_(nullptr), | |
| samples_10ms_(160), | |
| frequency_(16000), | |
| end_of_file_(false), | |
| auto_rewind_(false), | |
| rewinded_(false), | |
| read_stereo_(false), | |
| save_stereo_(false) { | |
| timestamp_ = | |
| (((uint32_t)rand() & 0x0000FFFF) << 16) | ((uint32_t)rand() & 0x0000FFFF); | |
| } | |
| PCMFile::PCMFile(uint32_t timestamp) | |
| : pcm_file_(nullptr), | |
| samples_10ms_(160), | |
| frequency_(16000), | |
| end_of_file_(false), | |
| auto_rewind_(false), | |
| rewinded_(false), | |
| read_stereo_(false), | |
| save_stereo_(false) { | |
| timestamp_ = timestamp; | |
| } | |
| PCMFile::~PCMFile() { | |
| if (pcm_file_) { | |
| fclose(pcm_file_); | |
| } | |
| } | |
| int16_t PCMFile::ChooseFile(std::string* file_name, | |
| int16_t max_len, | |
| uint16_t* frequency_hz) { | |
| char tmp_name[MAX_FILE_NAME_LENGTH_BYTE]; | |
| EXPECT_TRUE(fgets(tmp_name, MAX_FILE_NAME_LENGTH_BYTE, stdin) != nullptr); | |
| tmp_name[MAX_FILE_NAME_LENGTH_BYTE - 1] = '\0'; | |
| int16_t n = 0; | |
| // Removing trailing spaces. | |
| while ((isspace(static_cast<unsigned char>(tmp_name[n])) || | |
| iscntrl(static_cast<unsigned char>(tmp_name[n]))) && | |
| (static_cast<unsigned char>(tmp_name[n]) != 0) && | |
| (n < MAX_FILE_NAME_LENGTH_BYTE)) { | |
| n++; | |
| } | |
| if (n > 0) { | |
| memmove(tmp_name, &tmp_name[n], MAX_FILE_NAME_LENGTH_BYTE - n); | |
| } | |
| // Removing trailing spaces. | |
| n = (int16_t)(strlen(tmp_name) - 1); | |
| if (n >= 0) { | |
| while ((isspace(static_cast<unsigned char>(tmp_name[n])) || | |
| iscntrl(static_cast<unsigned char>(tmp_name[n]))) && | |
| (n >= 0)) { | |
| n--; | |
| } | |
| } | |
| if (n >= 0) { | |
| tmp_name[n + 1] = '\0'; | |
| } | |
| int16_t len = (int16_t)strlen(tmp_name); | |
| if (len > max_len) { | |
| return -1; | |
| } | |
| if (len > 0) { | |
| std::string tmp_string(tmp_name, len + 1); | |
| *file_name = tmp_string; | |
| } | |
| printf("Enter the sampling frequency (in Hz) of the above file [%u]: ", | |
| *frequency_hz); | |
| EXPECT_TRUE(fgets(tmp_name, 10, stdin) != nullptr); | |
| uint16_t tmp_frequency = (uint16_t)atoi(tmp_name); | |
| if (tmp_frequency > 0) { | |
| *frequency_hz = tmp_frequency; | |
| } | |
| return 0; | |
| } | |
| void PCMFile::Open(absl::string_view file_name, | |
| uint16_t frequency, | |
| absl::string_view mode, | |
| bool auto_rewind) { | |
| if ((pcm_file_ = fopen(std::string(file_name).c_str(), | |
| std::string(mode).c_str())) == nullptr) { | |
| printf("Cannot open file %s.\n", std::string(file_name).c_str()); | |
| ADD_FAILURE() << "Unable to read file"; | |
| } | |
| frequency_ = frequency; | |
| samples_10ms_ = (uint16_t)(frequency_ / 100); | |
| auto_rewind_ = auto_rewind; | |
| end_of_file_ = false; | |
| rewinded_ = false; | |
| } | |
| int32_t PCMFile::SamplingFrequency() const { | |
| return frequency_; | |
| } | |
| uint16_t PCMFile::PayloadLength10Ms() const { | |
| return samples_10ms_; | |
| } | |
| int32_t PCMFile::Read10MsData(AudioFrame& audio_frame) { | |
| uint16_t channels = 1; | |
| if (read_stereo_) { | |
| channels = 2; | |
| } | |
| int32_t payload_size = | |
| (int32_t)fread(audio_frame.mutable_data(), sizeof(uint16_t), | |
| samples_10ms_ * channels, pcm_file_); | |
| if (payload_size < samples_10ms_ * channels) { | |
| int16_t* frame_data = audio_frame.mutable_data(); | |
| for (int k = payload_size; k < samples_10ms_ * channels; k++) { | |
| frame_data[k] = 0; | |
| } | |
| if (auto_rewind_) { | |
| rewind(pcm_file_); | |
| rewinded_ = true; | |
| } else { | |
| end_of_file_ = true; | |
| } | |
| } | |
| audio_frame.samples_per_channel_ = samples_10ms_; | |
| audio_frame.sample_rate_hz_ = frequency_; | |
| audio_frame.num_channels_ = channels; | |
| audio_frame.timestamp_ = timestamp_; | |
| timestamp_ += samples_10ms_; | |
| ++blocks_read_; | |
| if (num_10ms_blocks_to_read_ && blocks_read_ >= *num_10ms_blocks_to_read_) | |
| end_of_file_ = true; | |
| return samples_10ms_; | |
| } | |
| void PCMFile::Write10MsData(const AudioFrame& audio_frame) { | |
| if (audio_frame.num_channels_ == 1) { | |
| if (!save_stereo_) { | |
| if (fwrite(audio_frame.data(), sizeof(uint16_t), | |
| audio_frame.samples_per_channel_, pcm_file_) != | |
| static_cast<size_t>(audio_frame.samples_per_channel_)) { | |
| return; | |
| } | |
| } else { | |
| const int16_t* frame_data = audio_frame.data(); | |
| int16_t* stereo_audio = new int16_t[2 * audio_frame.samples_per_channel_]; | |
| for (size_t k = 0; k < audio_frame.samples_per_channel_; k++) { | |
| stereo_audio[k << 1] = frame_data[k]; | |
| stereo_audio[(k << 1) + 1] = frame_data[k]; | |
| } | |
| if (fwrite(stereo_audio, sizeof(int16_t), | |
| 2 * audio_frame.samples_per_channel_, pcm_file_) != | |
| static_cast<size_t>(2 * audio_frame.samples_per_channel_)) { | |
| return; | |
| } | |
| delete[] stereo_audio; | |
| } | |
| } else { | |
| if (fwrite(audio_frame.data(), sizeof(int16_t), | |
| audio_frame.num_channels_ * audio_frame.samples_per_channel_, | |
| pcm_file_) != | |
| static_cast<size_t>(audio_frame.num_channels_ * | |
| audio_frame.samples_per_channel_)) { | |
| return; | |
| } | |
| } | |
| } | |
| void PCMFile::Write10MsData(const int16_t* playout_buffer, | |
| size_t length_smpls) { | |
| if (fwrite(playout_buffer, sizeof(uint16_t), length_smpls, pcm_file_) != | |
| length_smpls) { | |
| return; | |
| } | |
| } | |
| void PCMFile::Close() { | |
| fclose(pcm_file_); | |
| pcm_file_ = nullptr; | |
| blocks_read_ = 0; | |
| } | |
| void PCMFile::FastForward(int num_10ms_blocks) { | |
| const int channels = read_stereo_ ? 2 : 1; | |
| long num_bytes_to_move = | |
| num_10ms_blocks * sizeof(int16_t) * samples_10ms_ * channels; | |
| int error = fseek(pcm_file_, num_bytes_to_move, SEEK_CUR); | |
| RTC_DCHECK_EQ(error, 0); | |
| } | |
| void PCMFile::Rewind() { | |
| rewind(pcm_file_); | |
| end_of_file_ = false; | |
| blocks_read_ = 0; | |
| } | |
| bool PCMFile::Rewinded() { | |
| return rewinded_; | |
| } | |
| void PCMFile::SaveStereo(bool is_stereo) { | |
| save_stereo_ = is_stereo; | |
| } | |
| void PCMFile::ReadStereo(bool is_stereo) { | |
| read_stereo_ = is_stereo; | |
| } | |
| void PCMFile::SetNum10MsBlocksToRead(int value) { | |
| num_10ms_blocks_to_read_ = value; | |
| } | |
| } // namespace webrtc |