diff options
Diffstat (limited to 'www/chromium/files/sndio_input.cc')
-rw-r--r-- | www/chromium/files/sndio_input.cc | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/www/chromium/files/sndio_input.cc b/www/chromium/files/sndio_input.cc new file mode 100644 index 000000000000..948fb1089f7c --- /dev/null +++ b/www/chromium/files/sndio_input.cc @@ -0,0 +1,190 @@ +// Copyright 2013 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. + +#include "media/audio/sndio/sndio_input.h" + +#include <stddef.h> + +#include "base/bind.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/message_loop/message_loop.h" +#include "media/audio/openbsd/audio_manager_openbsd.h" +#include "media/audio/audio_manager.h" + +namespace media { + +void sndio_in_onmove(void *arg, int delta) { + NOTIMPLEMENTED(); + SndioAudioInputStream* self = static_cast<SndioAudioInputStream*>(arg); + + self->hw_delay_ = delta - self->params_.GetBytesPerFrame(); +} + +void *sndio_in_threadstart(void *arg) { + NOTIMPLEMENTED(); + SndioAudioInputStream* self = static_cast<SndioAudioInputStream*>(arg); + + self->ReadAudio(); + return NULL; +} + +SndioAudioInputStream::SndioAudioInputStream(AudioManagerBase* audio_manager, + const std::string& device_name, + const AudioParameters& params) + : audio_manager_(audio_manager), + device_name_(device_name), + params_(params), + bytes_per_buffer_(params.frames_per_buffer() * + (params.channels() * params.bits_per_sample()) / + 8), + buffer_duration_(base::TimeDelta::FromMicroseconds( + params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond / + static_cast<float>(params.sample_rate()))), + callback_(NULL), + device_handle_(NULL), + read_callback_behind_schedule_(false), + audio_bus_(AudioBus::Create(params)) { +} + +SndioAudioInputStream::~SndioAudioInputStream() {} + +bool SndioAudioInputStream::Open() { + struct sio_par par; + int sig; + + if (device_handle_) + return false; // Already open. + + if (params_.format() != AudioParameters::AUDIO_PCM_LINEAR && + params_.format() != AudioParameters::AUDIO_PCM_LOW_LATENCY) { + LOG(WARNING) << "Unsupported audio format."; + return false; + } + + sio_initpar(&par); + par.rate = params_.sample_rate(); + par.pchan = params_.channels(); + par.bits = params_.bits_per_sample(); + par.bps = par.bits / 8; + par.sig = sig = par.bits != 8 ? 1 : 0; + par.le = SIO_LE_NATIVE; + par.appbufsz = params_.frames_per_buffer(); + sndio_rec_bufsz_ = par.bufsz; + sndio_rec_bufsize_ = par.round * par.bps * par.rchan; + + device_handle_ = sio_open(SIO_DEVANY, SIO_REC, 0); + + if (device_handle_ == NULL) { + LOG(ERROR) << "Couldn't open audio device."; + return false; + } + + if (!sio_setpar(device_handle_, &par) || !sio_getpar(device_handle_, &par)) { + LOG(ERROR) << "Couldn't set audio parameters."; + goto bad_close; + } + + if (par.rate != (unsigned int)params_.sample_rate() || + par.pchan != (unsigned int)params_.channels() || + par.bits != (unsigned int)params_.bits_per_sample() || + par.sig != (unsigned int)sig || + (par.bps > 1 && par.le != SIO_LE_NATIVE) || + (par.bits != par.bps * 8)) { + LOG(ERROR) << "Unsupported audio parameters."; + goto bad_close; + } + sio_onmove(device_handle_, sndio_in_onmove, this); + + audio_buffer_.reset(new uint8_t[bytes_per_buffer_]); + + return true; +bad_close: + sio_close(device_handle_); + return false; +} + +void SndioAudioInputStream::Start(AudioInputCallback* callback) { + DCHECK(!callback_ && callback); + callback_ = callback; + StartAgc(); + + // We start reading data half |buffer_duration_| later than when the + // buffer might have got filled, to accommodate some delays in the audio + // driver. This could also give us a smooth read sequence going forward. + base::TimeDelta delay = buffer_duration_ + buffer_duration_ / 2; + next_read_time_ = base::TimeTicks::Now() + delay; + if (pthread_create(&thread_, NULL, sndio_in_threadstart, this) != 0) + LOG(ERROR) << "Failed to create real-time thread."; +} + +void SndioAudioInputStream::ReadAudio() { + NOTIMPLEMENTED(); + DCHECK(callback_); + + int num_buffers = sndio_rec_bufsize_ / params_.frames_per_buffer(); + double normalized_volume = 0.0; + + // Update the AGC volume level once every second. Note that, |volume| is + // also updated each time SetVolume() is called through IPC by the + // render-side AGC. + GetAgcVolume(&normalized_volume); + + while (num_buffers--) { + int frames_read = sio_read(device_handle_, audio_buffer_.get(), + params_.frames_per_buffer()); + if (frames_read == params_.frames_per_buffer()) { + audio_bus_->FromInterleaved(audio_buffer_.get(), + audio_bus_->frames(), + params_.bits_per_sample() / 8); + callback_->OnData( + this, audio_bus_.get(), hw_delay_, normalized_volume); + } else { + LOG(WARNING) << "sio_read() returning less than expected frames: " + << frames_read << " vs. " << params_.frames_per_buffer() + << ". Dropping this buffer."; + } + } +} + +void SndioAudioInputStream::Stop() { + if (!device_handle_ || !callback_) + return; + + StopAgc(); + + pthread_join(thread_, NULL); + sio_stop(device_handle_); + + callback_ = NULL; +} + +void SndioAudioInputStream::Close() { + if (device_handle_) { + sio_close(device_handle_); + audio_buffer_.reset(); + device_handle_ = NULL; + } + + audio_manager_->ReleaseInputStream(this); +} + +double SndioAudioInputStream::GetMaxVolume() { + return static_cast<double>(SIO_MAXVOL); +} + +void SndioAudioInputStream::SetVolume(double volume) { + NOTIMPLEMENTED(); +} + +double SndioAudioInputStream::GetVolume() { + long current_volume = 0; + return static_cast<double>(current_volume); +} + +bool SndioAudioInputStream::IsMuted() { + return false; +} + +} // namespace media |