This source file includes following definitions.
- BufferSize
- GetBuffer
- audio_bus_
- Open
- SetupBuffers
- FreeBuffers
- Start
- Stop
- Close
- SetVolume
- GetVolume
- HandleError
- QueueNextPacket
- BufferCallback
#include "media/audio/win/waveout_output_win.h"
#include <windows.h>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")
#include "base/atomicops.h"
#include "base/basictypes.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "media/audio/audio_io.h"
#include "media/audio/win/audio_manager_win.h"
namespace media {
static const uint32 kMaxOpenBufferSize = 1024 * 1024 * 64;
static const int kMaxChannelsToMask = 8;
static const unsigned int kChannelsToMask[kMaxChannelsToMask + 1] = {
0,
SPEAKER_FRONT_CENTER,
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT,
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER,
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER |
SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT |
SPEAKER_BACK_CENTER,
SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT |
SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT
};
inline size_t PCMWaveOutAudioOutputStream::BufferSize() const {
return (sizeof(WAVEHDR) + buffer_size_ + 15u) & static_cast<size_t>(~15);
}
inline WAVEHDR* PCMWaveOutAudioOutputStream::GetBuffer(int n) const {
DCHECK_GE(n, 0);
DCHECK_LT(n, num_buffers_);
return reinterpret_cast<WAVEHDR*>(&buffers_[n * BufferSize()]);
}
PCMWaveOutAudioOutputStream::PCMWaveOutAudioOutputStream(
AudioManagerWin* manager, const AudioParameters& params, int num_buffers,
UINT device_id)
: state_(PCMA_BRAND_NEW),
manager_(manager),
device_id_(device_id),
waveout_(NULL),
callback_(NULL),
num_buffers_(num_buffers),
buffer_size_(params.GetBytesPerBuffer()),
volume_(1),
channels_(params.channels()),
pending_bytes_(0),
waiting_handle_(NULL),
audio_bus_(AudioBus::Create(params)) {
format_.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
format_.Format.nChannels = params.channels();
format_.Format.nSamplesPerSec = params.sample_rate();
format_.Format.wBitsPerSample = params.bits_per_sample();
format_.Format.cbSize = sizeof(format_) - sizeof(WAVEFORMATEX);
format_.Format.nBlockAlign = (format_.Format.nChannels *
format_.Format.wBitsPerSample) / 8;
format_.Format.nAvgBytesPerSec = format_.Format.nBlockAlign *
format_.Format.nSamplesPerSec;
if (params.channels() > kMaxChannelsToMask) {
format_.dwChannelMask = kChannelsToMask[kMaxChannelsToMask];
} else {
format_.dwChannelMask = kChannelsToMask[params.channels()];
}
format_.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
format_.Samples.wValidBitsPerSample = params.bits_per_sample();
}
PCMWaveOutAudioOutputStream::~PCMWaveOutAudioOutputStream() {
DCHECK(NULL == waveout_);
}
bool PCMWaveOutAudioOutputStream::Open() {
if (state_ != PCMA_BRAND_NEW)
return false;
if (BufferSize() * num_buffers_ > kMaxOpenBufferSize)
return false;
if (num_buffers_ < 2 || num_buffers_ > 5)
return false;
buffer_event_.Set(::CreateEvent(NULL,
FALSE,
FALSE,
NULL));
if (!buffer_event_.Get())
return false;
MMRESULT result = ::waveOutOpen(
&waveout_,
device_id_,
reinterpret_cast<LPCWAVEFORMATEX>(&format_),
reinterpret_cast<DWORD_PTR>(buffer_event_.Get()),
NULL,
CALLBACK_EVENT);
if (result != MMSYSERR_NOERROR)
return false;
SetupBuffers();
state_ = PCMA_READY;
return true;
}
void PCMWaveOutAudioOutputStream::SetupBuffers() {
buffers_.reset(new char[BufferSize() * num_buffers_]);
for (int ix = 0; ix != num_buffers_; ++ix) {
WAVEHDR* buffer = GetBuffer(ix);
buffer->lpData = reinterpret_cast<char*>(buffer) + sizeof(WAVEHDR);
buffer->dwBufferLength = buffer_size_;
buffer->dwBytesRecorded = 0;
buffer->dwFlags = WHDR_DONE;
buffer->dwLoops = 0;
::waveOutPrepareHeader(waveout_, buffer, sizeof(WAVEHDR));
}
}
void PCMWaveOutAudioOutputStream::FreeBuffers() {
for (int ix = 0; ix != num_buffers_; ++ix) {
::waveOutUnprepareHeader(waveout_, GetBuffer(ix), sizeof(WAVEHDR));
}
buffers_.reset();
}
void PCMWaveOutAudioOutputStream::Start(AudioSourceCallback* callback) {
if (state_ != PCMA_READY)
return;
callback_ = callback;
if (!::ResetEvent(buffer_event_.Get())) {
HandleError(MMSYSERR_ERROR);
return;
}
if (!::RegisterWaitForSingleObject(&waiting_handle_,
buffer_event_.Get(),
&BufferCallback,
this,
INFINITE,
WT_EXECUTEDEFAULT)) {
HandleError(MMSYSERR_ERROR);
waiting_handle_ = NULL;
return;
}
state_ = PCMA_PLAYING;
pending_bytes_ = 0;
for (int ix = 0; ix != num_buffers_; ++ix) {
WAVEHDR* buffer = GetBuffer(ix);
QueueNextPacket(buffer);
pending_bytes_ += buffer->dwBufferLength;
}
base::subtle::MemoryBarrier();
MMRESULT result = ::waveOutPause(waveout_);
if (result != MMSYSERR_NOERROR) {
HandleError(result);
return;
}
for (int ix = 0; ix != num_buffers_; ++ix) {
result = ::waveOutWrite(waveout_, GetBuffer(ix), sizeof(WAVEHDR));
if (result != MMSYSERR_NOERROR) {
HandleError(result);
break;
}
}
result = ::waveOutRestart(waveout_);
if (result != MMSYSERR_NOERROR) {
HandleError(result);
return;
}
}
void PCMWaveOutAudioOutputStream::Stop() {
if (state_ != PCMA_PLAYING)
return;
state_ = PCMA_STOPPING;
base::subtle::MemoryBarrier();
if (waiting_handle_) {
if (!::UnregisterWaitEx(waiting_handle_, INVALID_HANDLE_VALUE))
HandleError(::GetLastError());
waiting_handle_ = NULL;
}
MMRESULT res = ::waveOutReset(waveout_);
if (res != MMSYSERR_NOERROR)
HandleError(res);
base::AutoLock auto_lock(lock_);
for (int ix = 0; ix != num_buffers_; ++ix)
GetBuffer(ix)->dwFlags = WHDR_PREPARED;
callback_ = NULL;
state_ = PCMA_READY;
}
void PCMWaveOutAudioOutputStream::Close() {
Stop();
if (waveout_) {
FreeBuffers();
MMRESULT res = ::waveOutReset(waveout_);
DCHECK_EQ(res, static_cast<MMRESULT>(MMSYSERR_NOERROR));
res = ::waveOutClose(waveout_);
DCHECK_EQ(res, static_cast<MMRESULT>(MMSYSERR_NOERROR));
state_ = PCMA_CLOSED;
waveout_ = NULL;
}
manager_->ReleaseOutputStream(this);
}
void PCMWaveOutAudioOutputStream::SetVolume(double volume) {
if (!waveout_)
return;
volume_ = static_cast<float>(volume);
}
void PCMWaveOutAudioOutputStream::GetVolume(double* volume) {
if (!waveout_)
return;
*volume = volume_;
}
void PCMWaveOutAudioOutputStream::HandleError(MMRESULT error) {
DLOG(WARNING) << "PCMWaveOutAudio error " << error;
if (callback_)
callback_->OnError(this);
}
void PCMWaveOutAudioOutputStream::QueueNextPacket(WAVEHDR *buffer) {
DCHECK_EQ(channels_, format_.Format.nChannels);
int frames_filled = callback_->OnMoreData(
audio_bus_.get(), AudioBuffersState(pending_bytes_, 0));
uint32 used = frames_filled * audio_bus_->channels() *
format_.Format.wBitsPerSample / 8;
if (used <= buffer_size_) {
audio_bus_->Scale(volume_);
audio_bus_->ToInterleaved(
frames_filled, format_.Format.wBitsPerSample / 8, buffer->lpData);
buffer->dwBufferLength = used * format_.Format.nChannels / channels_;
} else {
HandleError(0);
return;
}
buffer->dwFlags = WHDR_PREPARED;
}
void NTAPI PCMWaveOutAudioOutputStream::BufferCallback(PVOID lpParameter,
BOOLEAN timer_fired) {
TRACE_EVENT0("audio", "PCMWaveOutAudioOutputStream::BufferCallback");
DCHECK(!timer_fired);
PCMWaveOutAudioOutputStream* stream =
reinterpret_cast<PCMWaveOutAudioOutputStream*>(lpParameter);
base::AutoLock auto_lock(stream->lock_);
if (stream->state_ != PCMA_PLAYING)
return;
for (int ix = 0; ix != stream->num_buffers_; ++ix) {
WAVEHDR* buffer = stream->GetBuffer(ix);
if (buffer->dwFlags & WHDR_DONE) {
stream->pending_bytes_ -= buffer->dwBufferLength;
stream->QueueNextPacket(buffer);
if (stream->state_ != PCMA_PLAYING)
return;
MMRESULT result = ::waveOutWrite(stream->waveout_,
buffer,
sizeof(WAVEHDR));
if (result != MMSYSERR_NOERROR)
stream->HandleError(result);
stream->pending_bytes_ += buffer->dwBufferLength;
}
}
}
}