15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "media/audio/win/waveout_output_win.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <windows.h>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <mmsystem.h>
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#pragma comment(lib, "winmm.lib")
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/atomicops.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/basictypes.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/debug/trace_event.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "media/audio/audio_io.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "media/audio/win/audio_manager_win.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace media {
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Some general thoughts about the waveOut API which is badly documented :
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - We use CALLBACK_EVENT mode in which XP signals events such as buffer
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//   releases.
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - We use RegisterWaitForSingleObject() so one of threads in thread pool
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//   automatically calls our callback that feeds more data to Windows.
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - Windows does not provide a way to query if the device is playing or paused
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//   thus it forces you to maintain state, which naturally is not exactly
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//   synchronized to the actual device state.
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Sixty four MB is the maximum buffer size per AudioOutputStream.
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const uint32 kMaxOpenBufferSize = 1024 * 1024 * 64;
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// See Also
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// http://www.thx.com/consumer/home-entertainment/home-theater/surround-sound-speaker-set-up/
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// http://en.wikipedia.org/wiki/Surround_sound
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const int kMaxChannelsToMask = 8;
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const unsigned int kChannelsToMask[kMaxChannelsToMask + 1] = {
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  0,
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // 1 = Mono
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SPEAKER_FRONT_CENTER,
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // 2 = Stereo
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SPEAKER_FRONT_LEFT  | SPEAKER_FRONT_RIGHT,
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // 3 = Stereo + Center
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SPEAKER_FRONT_LEFT  | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER,
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // 4 = Quad
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SPEAKER_FRONT_LEFT  | SPEAKER_FRONT_RIGHT |
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // 5 = 5.0
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SPEAKER_FRONT_LEFT  | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER |
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // 6 = 5.1
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SPEAKER_FRONT_LEFT  | SPEAKER_FRONT_RIGHT |
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT,
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // 7 = 6.1
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SPEAKER_FRONT_LEFT  | SPEAKER_FRONT_RIGHT |
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT |
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SPEAKER_BACK_CENTER,
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // 8 = 7.1
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SPEAKER_FRONT_LEFT  | SPEAKER_FRONT_RIGHT |
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT |
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(fbarchard): Add additional masks for 7.2 and beyond.
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)inline size_t PCMWaveOutAudioOutputStream::BufferSize() const {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Round size of buffer up to the nearest 16 bytes.
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (sizeof(WAVEHDR) + buffer_size_ + 15u) & static_cast<size_t>(~15);
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)inline WAVEHDR* PCMWaveOutAudioOutputStream::GetBuffer(int n) const {
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_GE(n, 0);
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_LT(n, num_buffers_);
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return reinterpret_cast<WAVEHDR*>(&buffers_[n * BufferSize()]);
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PCMWaveOutAudioOutputStream::PCMWaveOutAudioOutputStream(
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AudioManagerWin* manager, const AudioParameters& params, int num_buffers,
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    UINT device_id)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : state_(PCMA_BRAND_NEW),
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      manager_(manager),
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      device_id_(device_id),
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      waveout_(NULL),
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      callback_(NULL),
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      num_buffers_(num_buffers),
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      buffer_size_(params.GetBytesPerBuffer()),
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      volume_(1),
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      channels_(params.channels()),
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      pending_bytes_(0),
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      waiting_handle_(NULL),
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      audio_bus_(AudioBus::Create(params)) {
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  format_.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  format_.Format.nChannels = params.channels();
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  format_.Format.nSamplesPerSec = params.sample_rate();
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  format_.Format.wBitsPerSample = params.bits_per_sample();
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  format_.Format.cbSize = sizeof(format_) - sizeof(WAVEFORMATEX);
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The next are computed from above.
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  format_.Format.nBlockAlign = (format_.Format.nChannels *
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                format_.Format.wBitsPerSample) / 8;
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  format_.Format.nAvgBytesPerSec = format_.Format.nBlockAlign *
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   format_.Format.nSamplesPerSec;
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (params.channels() > kMaxChannelsToMask) {
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    format_.dwChannelMask = kChannelsToMask[kMaxChannelsToMask];
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    format_.dwChannelMask = kChannelsToMask[params.channels()];
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  format_.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  format_.Samples.wValidBitsPerSample = params.bits_per_sample();
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PCMWaveOutAudioOutputStream::~PCMWaveOutAudioOutputStream() {
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(NULL == waveout_);
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool PCMWaveOutAudioOutputStream::Open() {
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (state_ != PCMA_BRAND_NEW)
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (BufferSize() * num_buffers_ > kMaxOpenBufferSize)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (num_buffers_ < 2 || num_buffers_ > 5)
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create buffer event.
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  buffer_event_.Set(::CreateEvent(NULL,    // Security attributes.
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  FALSE,   // It will auto-reset.
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  FALSE,   // Initial state.
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  NULL));  // No name.
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!buffer_event_.Get())
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Open the device.
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We'll be getting buffer_event_ events when it's time to refill the buffer.
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MMRESULT result = ::waveOutOpen(
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      &waveout_,
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      device_id_,
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      reinterpret_cast<LPCWAVEFORMATEX>(&format_),
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      reinterpret_cast<DWORD_PTR>(buffer_event_.Get()),
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NULL,
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CALLBACK_EVENT);
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result != MMSYSERR_NOERROR)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetupBuffers();
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  state_ = PCMA_READY;
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PCMWaveOutAudioOutputStream::SetupBuffers() {
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  buffers_.reset(new char[BufferSize() * num_buffers_]);
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int ix = 0; ix != num_buffers_; ++ix) {
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    WAVEHDR* buffer = GetBuffer(ix);
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    buffer->lpData = reinterpret_cast<char*>(buffer) + sizeof(WAVEHDR);
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    buffer->dwBufferLength = buffer_size_;
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    buffer->dwBytesRecorded = 0;
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    buffer->dwFlags = WHDR_DONE;
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    buffer->dwLoops = 0;
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Tell windows sound drivers about our buffers. Not documented what
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // this does but we can guess that causes the OS to keep a reference to
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the memory pages so the driver can use them without worries.
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ::waveOutPrepareHeader(waveout_, buffer, sizeof(WAVEHDR));
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PCMWaveOutAudioOutputStream::FreeBuffers() {
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int ix = 0; ix != num_buffers_; ++ix) {
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ::waveOutUnprepareHeader(waveout_, GetBuffer(ix), sizeof(WAVEHDR));
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
170c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  buffers_.reset();
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Initially we ask the source to fill up all audio buffers. If we don't do
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// this then we would always get the driver callback when it is about to run
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// samples and that would leave too little time to react.
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PCMWaveOutAudioOutputStream::Start(AudioSourceCallback* callback) {
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (state_ != PCMA_READY)
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  callback_ = callback;
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Reset buffer event, it can be left in the arbitrary state if we
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // previously stopped the stream. Can happen because we are stopping
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // callbacks before stopping playback itself.
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!::ResetEvent(buffer_event_.Get())) {
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    HandleError(MMSYSERR_ERROR);
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Start watching for buffer events.
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!::RegisterWaitForSingleObject(&waiting_handle_,
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     buffer_event_.Get(),
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     &BufferCallback,
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     this,
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     INFINITE,
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     WT_EXECUTEDEFAULT)) {
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    HandleError(MMSYSERR_ERROR);
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    waiting_handle_ = NULL;
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  state_ = PCMA_PLAYING;
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Queue the buffers.
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  pending_bytes_ = 0;
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int ix = 0; ix != num_buffers_; ++ix) {
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    WAVEHDR* buffer = GetBuffer(ix);
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    QueueNextPacket(buffer);  // Read more data.
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    pending_bytes_ += buffer->dwBufferLength;
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // From now on |pending_bytes_| would be accessed by callback thread.
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Most likely waveOutPause() or waveOutRestart() has its own memory barrier,
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // but issuing our own is safer.
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::subtle::MemoryBarrier();
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MMRESULT result = ::waveOutPause(waveout_);
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result != MMSYSERR_NOERROR) {
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    HandleError(result);
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Send the buffers to the audio driver. Note that the device is paused
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // so we avoid entering the callback method while still here.
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int ix = 0; ix != num_buffers_; ++ix) {
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    result = ::waveOutWrite(waveout_, GetBuffer(ix), sizeof(WAVEHDR));
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (result != MMSYSERR_NOERROR) {
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      HandleError(result);
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  result = ::waveOutRestart(waveout_);
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (result != MMSYSERR_NOERROR) {
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    HandleError(result);
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Stopping is tricky if we want it be fast.
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// For now just do it synchronously and avoid all the complexities.
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO(enal): if we want faster Stop() we can create singleton that keeps track
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//             of all currently playing streams. Then you don't have to wait
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//             till all callbacks are completed. Of course access to singleton
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//             should be under its own lock, and checking the liveness and
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//             acquiring the lock on stream should be done atomically.
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PCMWaveOutAudioOutputStream::Stop() {
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (state_ != PCMA_PLAYING)
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  state_ = PCMA_STOPPING;
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::subtle::MemoryBarrier();
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Stop watching for buffer event, waits until outstanding callbacks finish.
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (waiting_handle_) {
2535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (!::UnregisterWaitEx(waiting_handle_, INVALID_HANDLE_VALUE))
2545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      HandleError(::GetLastError());
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    waiting_handle_ = NULL;
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Stop playback.
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MMRESULT res = ::waveOutReset(waveout_);
2605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (res != MMSYSERR_NOERROR)
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    HandleError(res);
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Wait for lock to ensure all outstanding callbacks have completed.
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::AutoLock auto_lock(lock_);
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // waveOutReset() leaves buffers in the unpredictable state, causing
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // problems if we want to close, release, or reuse them. Fix the states.
2685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (int ix = 0; ix != num_buffers_; ++ix)
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GetBuffer(ix)->dwFlags = WHDR_PREPARED;
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Don't use callback after Stop().
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  callback_ = NULL;
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  state_ = PCMA_READY;
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// We can Close in any state except that trying to close a stream that is
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// playing Windows generates an error. We cannot propagate it to the source,
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// as callback_ is set to NULL. Just print it and hope somebody somehow
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// will find it...
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PCMWaveOutAudioOutputStream::Close() {
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Force Stop() to ensure it's safe to release buffers and free the stream.
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Stop();
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (waveout_) {
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    FreeBuffers();
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // waveOutClose() generates a WIM_CLOSE callback.  In case Start() was never
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // called, force a reset to ensure close succeeds.
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    MMRESULT res = ::waveOutReset(waveout_);
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK_EQ(res, static_cast<MMRESULT>(MMSYSERR_NOERROR));
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    res = ::waveOutClose(waveout_);
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK_EQ(res, static_cast<MMRESULT>(MMSYSERR_NOERROR));
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    state_ = PCMA_CLOSED;
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    waveout_ = NULL;
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Tell the audio manager that we have been released. This can result in
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the manager destroying us in-place so this needs to be the last thing
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // we do on this function.
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  manager_->ReleaseOutputStream(this);
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PCMWaveOutAudioOutputStream::SetVolume(double volume) {
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!waveout_)
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  volume_ = static_cast<float>(volume);
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PCMWaveOutAudioOutputStream::GetVolume(double* volume) {
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!waveout_)
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *volume = volume_;
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PCMWaveOutAudioOutputStream::HandleError(MMRESULT error) {
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DLOG(WARNING) << "PCMWaveOutAudio error " << error;
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (callback_)
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    callback_->OnError(this);
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PCMWaveOutAudioOutputStream::QueueNextPacket(WAVEHDR *buffer) {
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(channels_, format_.Format.nChannels);
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Call the source which will fill our buffer with pleasant sounds and
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // return to us how many bytes were used.
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(fbarchard): Handle used 0 by queueing more.
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(sergeyu): Specify correct hardware delay for AudioBuffersState.
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int frames_filled = callback_->OnMoreData(
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      audio_bus_.get(), AudioBuffersState(pending_bytes_, 0));
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uint32 used = frames_filled * audio_bus_->channels() *
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      format_.Format.wBitsPerSample / 8;
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (used <= buffer_size_) {
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Note: If this ever changes to output raw float the data must be clipped
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // and sanitized since it may come from an untrusted source such as NaCl.
337c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    audio_bus_->Scale(volume_);
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    audio_bus_->ToInterleaved(
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        frames_filled, format_.Format.wBitsPerSample / 8, buffer->lpData);
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    buffer->dwBufferLength = used * format_.Format.nChannels / channels_;
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    HandleError(0);
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  buffer->dwFlags = WHDR_PREPARED;
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// One of the threads in our thread pool asynchronously calls this function when
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// buffer_event_ is signalled. Search through all the buffers looking for freed
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// ones, fills them with data, and "feed" the Windows.
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Note: by searching through all the buffers we guarantee that we fill all the
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//       buffers, even when "event loss" happens, i.e. if Windows signals event
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//       when it did not flip into unsignaled state from the previous signal.
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void NTAPI PCMWaveOutAudioOutputStream::BufferCallback(PVOID lpParameter,
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                       BOOLEAN timer_fired) {
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TRACE_EVENT0("audio", "PCMWaveOutAudioOutputStream::BufferCallback");
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!timer_fired);
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PCMWaveOutAudioOutputStream* stream =
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      reinterpret_cast<PCMWaveOutAudioOutputStream*>(lpParameter);
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Lock the stream so callbacks do not interfere with each other.
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Several callbacks can be called simultaneously by different threads in the
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // thread pool if some of the callbacks are slow, or system is very busy and
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // scheduled callbacks are not called on time.
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::AutoLock auto_lock(stream->lock_);
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (stream->state_ != PCMA_PLAYING)
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (int ix = 0; ix != stream->num_buffers_; ++ix) {
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    WAVEHDR* buffer = stream->GetBuffer(ix);
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (buffer->dwFlags & WHDR_DONE) {
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Before we queue the next packet, we need to adjust the number of
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // pending bytes since the last write to hardware.
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      stream->pending_bytes_ -= buffer->dwBufferLength;
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      stream->QueueNextPacket(buffer);
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // QueueNextPacket() can take a long time, especially if several of them
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // were called back-to-back. Check if we are stopping now.
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (stream->state_ != PCMA_PLAYING)
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Time to send the buffer to the audio driver. Since we are reusing
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // the same buffers we can get away without calling waveOutPrepareHeader.
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      MMRESULT result = ::waveOutWrite(stream->waveout_,
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       buffer,
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       sizeof(WAVEHDR));
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (result != MMSYSERR_NOERROR)
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        stream->HandleError(result);
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      stream->pending_bytes_ += buffer->dwBufferLength;
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace media
397