wavein_input_win.cc revision 868fa2fe829687343ffae624259930155e16dbd8
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "media/audio/win/wavein_input_win.h" 6 7#pragma comment(lib, "winmm.lib") 8 9#include "base/logging.h" 10#include "media/audio/audio_io.h" 11#include "media/audio/audio_util.h" 12#include "media/audio/win/audio_manager_win.h" 13#include "media/audio/win/device_enumeration_win.h" 14 15namespace media { 16 17// Our sound buffers are allocated once and kept in a linked list using the 18// the WAVEHDR::dwUser variable. The last buffer points to the first buffer. 19static WAVEHDR* GetNextBuffer(WAVEHDR* current) { 20 return reinterpret_cast<WAVEHDR*>(current->dwUser); 21} 22 23PCMWaveInAudioInputStream::PCMWaveInAudioInputStream( 24 AudioManagerWin* manager, const AudioParameters& params, int num_buffers, 25 const std::string& device_id) 26 : state_(kStateEmpty), 27 manager_(manager), 28 device_id_(device_id), 29 wavein_(NULL), 30 callback_(NULL), 31 num_buffers_(num_buffers), 32 buffer_(NULL), 33 channels_(params.channels()) { 34 DCHECK_GT(num_buffers_, 0); 35 format_.wFormatTag = WAVE_FORMAT_PCM; 36 format_.nChannels = params.channels() > 2 ? 2 : params.channels(); 37 format_.nSamplesPerSec = params.sample_rate(); 38 format_.wBitsPerSample = params.bits_per_sample(); 39 format_.cbSize = 0; 40 format_.nBlockAlign = (format_.nChannels * format_.wBitsPerSample) / 8; 41 format_.nAvgBytesPerSec = format_.nBlockAlign * format_.nSamplesPerSec; 42 buffer_size_ = params.frames_per_buffer() * format_.nBlockAlign; 43 // If we don't have a packet size we use 100ms. 44 if (!buffer_size_) 45 buffer_size_ = format_.nAvgBytesPerSec / 10; 46 // The event is auto-reset. 47 stopped_event_.Set(::CreateEventW(NULL, FALSE, FALSE, NULL)); 48} 49 50PCMWaveInAudioInputStream::~PCMWaveInAudioInputStream() { 51 DCHECK(NULL == wavein_); 52} 53 54bool PCMWaveInAudioInputStream::Open() { 55 DCHECK(thread_checker_.CalledOnValidThread()); 56 if (state_ != kStateEmpty) 57 return false; 58 if (num_buffers_ < 2 || num_buffers_ > 10) 59 return false; 60 61 // Convert the stored device id string into an unsigned integer 62 // corresponding to the selected device. 63 UINT device_id = WAVE_MAPPER; 64 if (!GetDeviceId(&device_id)) { 65 return false; 66 } 67 68 // Open the specified input device for recording. 69 MMRESULT result = MMSYSERR_NOERROR; 70 result = ::waveInOpen(&wavein_, device_id, &format_, 71 reinterpret_cast<DWORD_PTR>(WaveCallback), 72 reinterpret_cast<DWORD_PTR>(this), 73 CALLBACK_FUNCTION); 74 if (result != MMSYSERR_NOERROR) 75 return false; 76 77 SetupBuffers(); 78 state_ = kStateReady; 79 return true; 80} 81 82void PCMWaveInAudioInputStream::SetupBuffers() { 83 WAVEHDR* last = NULL; 84 WAVEHDR* first = NULL; 85 for (int ix = 0; ix != num_buffers_; ++ix) { 86 uint32 sz = sizeof(WAVEHDR) + buffer_size_; 87 buffer_ = reinterpret_cast<WAVEHDR*>(new char[sz]); 88 buffer_->lpData = reinterpret_cast<char*>(buffer_) + sizeof(WAVEHDR); 89 buffer_->dwBufferLength = buffer_size_; 90 buffer_->dwBytesRecorded = 0; 91 buffer_->dwUser = reinterpret_cast<DWORD_PTR>(last); 92 buffer_->dwFlags = WHDR_DONE; 93 buffer_->dwLoops = 0; 94 if (ix == 0) 95 first = buffer_; 96 last = buffer_; 97 ::waveInPrepareHeader(wavein_, buffer_, sizeof(WAVEHDR)); 98 } 99 // Fix the first buffer to point to the last one. 100 first->dwUser = reinterpret_cast<DWORD_PTR>(last); 101} 102 103void PCMWaveInAudioInputStream::FreeBuffers() { 104 WAVEHDR* current = buffer_; 105 for (int ix = 0; ix != num_buffers_; ++ix) { 106 WAVEHDR* next = GetNextBuffer(current); 107 if (current->dwFlags & WHDR_PREPARED) 108 ::waveInUnprepareHeader(wavein_, current, sizeof(WAVEHDR)); 109 delete[] reinterpret_cast<char*>(current); 110 current = next; 111 } 112 buffer_ = NULL; 113} 114 115void PCMWaveInAudioInputStream::Start(AudioInputCallback* callback) { 116 DCHECK(thread_checker_.CalledOnValidThread()); 117 if (state_ != kStateReady) 118 return; 119 120 DCHECK(!callback_); 121 callback_ = callback; 122 state_ = kStateRecording; 123 124 WAVEHDR* buffer = buffer_; 125 for (int ix = 0; ix != num_buffers_; ++ix) { 126 QueueNextPacket(buffer); 127 buffer = GetNextBuffer(buffer); 128 } 129 buffer = buffer_; 130 131 MMRESULT result = ::waveInStart(wavein_); 132 if (result != MMSYSERR_NOERROR) { 133 HandleError(result); 134 state_ = kStateReady; 135 callback_ = NULL; 136 } 137} 138 139// Stopping is tricky. First, no buffer should be locked by the audio driver 140// or else the waveInReset() will deadlock and secondly, the callback should 141// not be inside the AudioInputCallback's OnData because waveInReset() 142// forcefully kills the callback thread. 143void PCMWaveInAudioInputStream::Stop() { 144 DVLOG(1) << "PCMWaveInAudioInputStream::Stop()"; 145 DCHECK(thread_checker_.CalledOnValidThread()); 146 if (state_ != kStateRecording) 147 return; 148 149 bool already_stopped = false; 150 { 151 // Tell the callback that we're stopping. 152 // As a result, |stopped_event_| will be signaled in callback method. 153 base::AutoLock auto_lock(lock_); 154 already_stopped = (callback_ == NULL); 155 callback_ = NULL; 156 } 157 158 if (already_stopped) 159 return; 160 161 // Wait for the callback to finish, it will signal us when ready to be reset. 162 DWORD wait = ::WaitForSingleObject(stopped_event_, INFINITE); 163 DCHECK_EQ(wait, WAIT_OBJECT_0); 164 165 // Stop input and reset the current position to zero for |wavein_|. 166 // All pending buffers are marked as done and returned to the application. 167 MMRESULT res = ::waveInReset(wavein_); 168 DCHECK_EQ(res, static_cast<MMRESULT>(MMSYSERR_NOERROR)); 169 170 state_ = kStateReady; 171} 172 173void PCMWaveInAudioInputStream::Close() { 174 DVLOG(1) << "PCMWaveInAudioInputStream::Close()"; 175 DCHECK(thread_checker_.CalledOnValidThread()); 176 177 // We should not call Close() while recording. Catch it with DCHECK and 178 // implement auto-stop just in case. 179 DCHECK_NE(state_, kStateRecording); 180 Stop(); 181 182 if (wavein_) { 183 FreeBuffers(); 184 185 // waveInClose() generates a WIM_CLOSE callback. In case Start() was never 186 // called, force a reset to ensure close succeeds. 187 MMRESULT res = ::waveInReset(wavein_); 188 DCHECK_EQ(res, static_cast<MMRESULT>(MMSYSERR_NOERROR)); 189 res = ::waveInClose(wavein_); 190 DCHECK_EQ(res, static_cast<MMRESULT>(MMSYSERR_NOERROR)); 191 state_ = kStateClosed; 192 wavein_ = NULL; 193 } 194 195 // Tell the audio manager that we have been released. This can result in 196 // the manager destroying us in-place so this needs to be the last thing 197 // we do on this function. 198 manager_->ReleaseInputStream(this); 199} 200 201double PCMWaveInAudioInputStream::GetMaxVolume() { 202 // TODO(henrika): Add volume support using the Audio Mixer API. 203 return 0.0; 204} 205 206void PCMWaveInAudioInputStream::SetVolume(double volume) { 207 // TODO(henrika): Add volume support using the Audio Mixer API. 208} 209 210double PCMWaveInAudioInputStream::GetVolume() { 211 // TODO(henrika): Add volume support using the Audio Mixer API. 212 return 0.0; 213} 214 215void PCMWaveInAudioInputStream::SetAutomaticGainControl(bool enabled) { 216 // TODO(henrika): Add AGC support when volume control has been added. 217 NOTIMPLEMENTED(); 218} 219 220bool PCMWaveInAudioInputStream::GetAutomaticGainControl() { 221 // TODO(henrika): Add AGC support when volume control has been added. 222 NOTIMPLEMENTED(); 223 return false; 224} 225 226void PCMWaveInAudioInputStream::HandleError(MMRESULT error) { 227 DLOG(WARNING) << "PCMWaveInAudio error " << error; 228 callback_->OnError(this); 229} 230 231void PCMWaveInAudioInputStream::QueueNextPacket(WAVEHDR *buffer) { 232 MMRESULT res = ::waveInAddBuffer(wavein_, buffer, sizeof(WAVEHDR)); 233 if (res != MMSYSERR_NOERROR) 234 HandleError(res); 235} 236 237bool PCMWaveInAudioInputStream::GetDeviceId(UINT* device_index) { 238 // Deliver the default input device id (WAVE_MAPPER) if the default 239 // device has been selected. 240 if (device_id_ == AudioManagerBase::kDefaultDeviceId) { 241 *device_index = WAVE_MAPPER; 242 return true; 243 } 244 245 // Get list of all available and active devices. 246 AudioDeviceNames device_names; 247 if (!media::GetInputDeviceNamesWinXP(&device_names)) 248 return false; 249 250 if (device_names.empty()) 251 return false; 252 253 // Search the full list of devices and compare with the specified 254 // device id which was specified in the constructor. Stop comparing 255 // when a match is found and return the corresponding index. 256 UINT index = 0; 257 bool found_device = false; 258 AudioDeviceNames::const_iterator it = device_names.begin(); 259 while (it != device_names.end()) { 260 if (it->unique_id.compare(device_id_) == 0) { 261 *device_index = index; 262 found_device = true; 263 break; 264 } 265 ++index; 266 ++it; 267 } 268 269 return found_device; 270} 271 272// Windows calls us back in this function when some events happen. Most notably 273// when it has an audio buffer with recorded data. 274void PCMWaveInAudioInputStream::WaveCallback(HWAVEIN hwi, UINT msg, 275 DWORD_PTR instance, 276 DWORD_PTR param1, DWORD_PTR) { 277 PCMWaveInAudioInputStream* obj = 278 reinterpret_cast<PCMWaveInAudioInputStream*>(instance); 279 280 // The lock ensures that Stop() can't be called during a callback. 281 base::AutoLock auto_lock(obj->lock_); 282 283 if (msg == WIM_DATA) { 284 // The WIM_DATA message is sent when waveform-audio data is present in 285 // the input buffer and the buffer is being returned to the application. 286 // The message can be sent when the buffer is full or after the 287 // waveInReset function is called. 288 if (obj->callback_) { 289 // TODO(henrika): the |volume| parameter is always set to zero since 290 // there is currently no support for controlling the microphone volume 291 // level. 292 WAVEHDR* buffer = reinterpret_cast<WAVEHDR*>(param1); 293 obj->callback_->OnData(obj, 294 reinterpret_cast<const uint8*>(buffer->lpData), 295 buffer->dwBytesRecorded, 296 buffer->dwBytesRecorded, 297 0.0); 298 299 // Queue the finished buffer back with the audio driver. Since we are 300 // reusing the same buffers we can get away without calling 301 // waveInPrepareHeader. 302 obj->QueueNextPacket(buffer); 303 } else { 304 // Main thread has called Stop() and set |callback_| to NULL and is 305 // now waiting to issue waveInReset which will kill this thread. 306 // We should not call AudioSourceCallback code anymore. 307 ::SetEvent(obj->stopped_event_); 308 } 309 } else if (msg == WIM_CLOSE) { 310 // Intentionaly no-op for now. 311 } else if (msg == WIM_OPEN) { 312 // Intentionaly no-op for now. 313 } 314} 315 316} // namespace media 317