// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // MSVC++ requires this to be set before any other includes to get M_PI. #define _USE_MATH_DEFINES #include "media/audio/simple_sources.h" #include #include #include #include "base/files/file.h" #include "base/logging.h" #include "base/time/time.h" #include "media/audio/sounds/wav_audio_handler.h" #include "media/base/audio_bus.h" namespace media { namespace { // Opens |wav_filename|, reads it and loads it as a wav file. This function will // return a null pointer if we can't read the file or if it's malformed. The // caller takes ownership of the returned data. The size of the data is stored // in |read_length|. std::unique_ptr ReadWavFile(const base::FilePath& wav_filename, size_t* read_length) { base::File wav_file( wav_filename, base::File::FLAG_OPEN | base::File::FLAG_READ); if (!wav_file.IsValid()) { LOG(ERROR) << "Failed to read " << wav_filename.value() << " as input to the fake device."; return nullptr; } int64_t wav_file_length = wav_file.GetLength(); if (wav_file_length < 0) { LOG(ERROR) << "Failed to get size of " << wav_filename.value(); return nullptr; } if (wav_file_length == 0) { LOG(ERROR) << "Input file to fake device is empty: " << wav_filename.value(); return nullptr; } std::unique_ptr data(new char[wav_file_length]); int read_bytes = wav_file.Read(0, data.get(), wav_file_length); if (read_bytes != wav_file_length) { LOG(ERROR) << "Failed to read all bytes of " << wav_filename.value(); return nullptr; } *read_length = wav_file_length; return data; } // These values are based on experiments for local-to-local // PeerConnection to demonstrate audio/video synchronization. static const int kBeepDurationMilliseconds = 20; static const int kBeepFrequency = 400; // Intervals between two automatic beeps. static const int kAutomaticBeepIntervalInMs = 500; // Automatic beep will be triggered every |kAutomaticBeepIntervalInMs| unless // users explicitly call BeepOnce(), which will disable the automatic beep. class BeepContext { public: BeepContext() : beep_once_(false), automatic_beep_(true) {} void SetBeepOnce(bool enable) { base::AutoLock auto_lock(lock_); beep_once_ = enable; // Disable the automatic beep if users explicit set |beep_once_| to true. if (enable) automatic_beep_ = false; } bool beep_once() const { base::AutoLock auto_lock(lock_); return beep_once_; } bool automatic_beep() const { base::AutoLock auto_lock(lock_); return automatic_beep_; } private: mutable base::Lock lock_; bool beep_once_; bool automatic_beep_; }; BeepContext* GetBeepContext() { static BeepContext* context = new BeepContext(); return context; } } // namespace ////////////////////////////////////////////////////////////////////////////// // SineWaveAudioSource implementation. SineWaveAudioSource::SineWaveAudioSource(int channels, double freq, double sample_freq) : channels_(channels), f_(freq / sample_freq), time_state_(0), cap_(0), callbacks_(0), errors_(0) { } SineWaveAudioSource::~SineWaveAudioSource() { } // The implementation could be more efficient if a lookup table is constructed // but it is efficient enough for our simple needs. int SineWaveAudioSource::OnMoreData(base::TimeDelta /* delay */, base::TimeTicks /* delay_timestamp */, int /* prior_frames_skipped */, AudioBus* dest) { base::AutoLock auto_lock(time_lock_); callbacks_++; // The table is filled with s(t) = kint16max*sin(Theta*t), // where Theta = 2*PI*fs. // We store the discrete time value |t| in a member to ensure that the // next pass starts at a correct state. int max_frames = cap_ > 0 ? std::min(dest->frames(), cap_ - time_state_) : dest->frames(); for (int i = 0; i < max_frames; ++i) dest->channel(0)[i] = sin(2.0 * M_PI * f_ * time_state_++); for (int i = 1; i < dest->channels(); ++i) { memcpy(dest->channel(i), dest->channel(0), max_frames * sizeof(*dest->channel(i))); } return max_frames; } void SineWaveAudioSource::OnError(AudioOutputStream* stream) { errors_++; } void SineWaveAudioSource::CapSamples(int cap) { base::AutoLock auto_lock(time_lock_); DCHECK_GT(cap, 0); cap_ = cap; } void SineWaveAudioSource::Reset() { base::AutoLock auto_lock(time_lock_); time_state_ = 0; } FileSource::FileSource(const AudioParameters& params, const base::FilePath& path_to_wav_file, bool loop) : params_(params), path_to_wav_file_(path_to_wav_file), wav_file_read_pos_(0), load_failed_(false), looping_(loop) {} FileSource::~FileSource() { } void FileSource::LoadWavFile(const base::FilePath& path_to_wav_file) { // Don't try again if we already failed. if (load_failed_) return; // Read the file, and put its data in a scoped_ptr so it gets deleted when // this class destructs. This data must be valid for the lifetime of // |wav_audio_handler_|. size_t length = 0u; raw_wav_data_ = ReadWavFile(path_to_wav_file, &length); if (!raw_wav_data_) { load_failed_ = true; return; } // Attempt to create a handler with this data. If the data is invalid, return. wav_audio_handler_ = WavAudioHandler::Create(base::StringPiece(raw_wav_data_.get(), length)); if (!wav_audio_handler_) { LOG(ERROR) << "WAV data could be read but is not valid"; load_failed_ = true; return; } // Hook us up so we pull in data from the file into the converter. We need to // modify the wav file's audio parameters since we'll be reading small slices // of it at a time and not the whole thing (like 10 ms at a time). AudioParameters file_audio_slice( AudioParameters::AUDIO_PCM_LOW_LATENCY, GuessChannelLayout(wav_audio_handler_->num_channels()), wav_audio_handler_->sample_rate(), wav_audio_handler_->bits_per_sample(), params_.frames_per_buffer()); file_audio_converter_.reset( new AudioConverter(file_audio_slice, params_, false)); file_audio_converter_->AddInput(this); } int FileSource::OnMoreData(base::TimeDelta /* delay */, base::TimeTicks /* delay_timestamp */, int /* prior_frames_skipped */, AudioBus* dest) { // Load the file if we haven't already. This load needs to happen on the // audio thread, otherwise we'll run on the UI thread on Mac for instance. // This will massively delay the first OnMoreData, but we'll catch up. if (!wav_audio_handler_) LoadWavFile(path_to_wav_file_); if (load_failed_) return 0; DCHECK(wav_audio_handler_.get()); if (wav_audio_handler_->AtEnd(wav_file_read_pos_)) { if (looping_) Rewind(); else return 0; } // This pulls data from ProvideInput. file_audio_converter_->Convert(dest); return dest->frames(); } void FileSource::Rewind() { wav_file_read_pos_ = 0; } double FileSource::ProvideInput(AudioBus* audio_bus_into_converter, uint32_t frames_delayed) { // Unfilled frames will be zeroed by CopyTo. size_t bytes_written; wav_audio_handler_->CopyTo(audio_bus_into_converter, wav_file_read_pos_, &bytes_written); wav_file_read_pos_ += bytes_written; return 1.0; } void FileSource::OnError(AudioOutputStream* stream) { } BeepingSource::BeepingSource(const AudioParameters& params) : buffer_size_(params.GetBytesPerBuffer()), buffer_(new uint8_t[buffer_size_]), params_(params), last_callback_time_(base::TimeTicks::Now()), beep_duration_in_buffers_(kBeepDurationMilliseconds * params.sample_rate() / params.frames_per_buffer() / 1000), beep_generated_in_buffers_(0), beep_period_in_frames_(params.sample_rate() / kBeepFrequency) {} BeepingSource::~BeepingSource() { } int BeepingSource::OnMoreData(base::TimeDelta /* delay */, base::TimeTicks /* delay_timestamp */, int /* prior_frames_skipped */, AudioBus* dest) { // Accumulate the time from the last beep. interval_from_last_beep_ += base::TimeTicks::Now() - last_callback_time_; memset(buffer_.get(), 0, buffer_size_); bool should_beep = false; BeepContext* beep_context = GetBeepContext(); if (beep_context->automatic_beep()) { base::TimeDelta delta = interval_from_last_beep_ - base::TimeDelta::FromMilliseconds(kAutomaticBeepIntervalInMs); if (delta > base::TimeDelta()) { should_beep = true; interval_from_last_beep_ = delta; } } else { should_beep = beep_context->beep_once(); beep_context->SetBeepOnce(false); } // If this object was instructed to generate a beep or has started to // generate a beep sound. if (should_beep || beep_generated_in_buffers_) { // Compute the number of frames to output high value. Then compute the // number of bytes based on channels and bits per channel. int high_frames = beep_period_in_frames_ / 2; int high_bytes = high_frames * params_.bits_per_sample() * params_.channels() / 8; // Separate high and low with the same number of bytes to generate a // square wave. int position = 0; while (position + high_bytes <= buffer_size_) { // Write high values first. memset(buffer_.get() + position, 128, high_bytes); // Then leave low values in the buffer with |high_bytes|. position += high_bytes * 2; } ++beep_generated_in_buffers_; if (beep_generated_in_buffers_ >= beep_duration_in_buffers_) beep_generated_in_buffers_ = 0; } last_callback_time_ = base::TimeTicks::Now(); dest->FromInterleaved(buffer_.get(), dest->frames(), params_.bits_per_sample() / 8); return dest->frames(); } void BeepingSource::OnError(AudioOutputStream* stream) { } void BeepingSource::BeepOnce() { GetBeepContext()->SetBeepOnce(true); } } // namespace media