wavein_input_win.cc revision 010d83a9304c5a91596085d917d248abff47903a
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/win/audio_manager_win.h" 12#include "media/audio/win/device_enumeration_win.h" 13 14namespace media { 15 16// Our sound buffers are allocated once and kept in a linked list using the 17// the WAVEHDR::dwUser variable. The last buffer points to the first buffer. 18static WAVEHDR* GetNextBuffer(WAVEHDR* current) { 19 return reinterpret_cast<WAVEHDR*>(current->dwUser); 20} 21 22PCMWaveInAudioInputStream::PCMWaveInAudioInputStream( 23 AudioManagerWin* manager, const AudioParameters& params, int num_buffers, 24 const std::string& device_id) 25 : state_(kStateEmpty), 26 manager_(manager), 27 device_id_(device_id), 28 wavein_(NULL), 29 callback_(NULL), 30 num_buffers_(num_buffers), 31 buffer_(NULL), 32 channels_(params.channels()) { 33 DCHECK_GT(num_buffers_, 0); 34 format_.wFormatTag = WAVE_FORMAT_PCM; 35 format_.nChannels = params.channels() > 2 ? 2 : params.channels(); 36 format_.nSamplesPerSec = params.sample_rate(); 37 format_.wBitsPerSample = params.bits_per_sample(); 38 format_.cbSize = 0; 39 format_.nBlockAlign = (format_.nChannels * format_.wBitsPerSample) / 8; 40 format_.nAvgBytesPerSec = format_.nBlockAlign * format_.nSamplesPerSec; 41 buffer_size_ = params.frames_per_buffer() * format_.nBlockAlign; 42 // If we don't have a packet size we use 100ms. 43 if (!buffer_size_) 44 buffer_size_ = format_.nAvgBytesPerSec / 10; 45 // The event is auto-reset. 46 stopped_event_.Set(::CreateEventW(NULL, FALSE, FALSE, NULL)); 47} 48 49PCMWaveInAudioInputStream::~PCMWaveInAudioInputStream() { 50 DCHECK(NULL == wavein_); 51} 52 53bool PCMWaveInAudioInputStream::Open() { 54 DCHECK(thread_checker_.CalledOnValidThread()); 55 if (state_ != kStateEmpty) 56 return false; 57 if (num_buffers_ < 2 || num_buffers_ > 10) 58 return false; 59 60 // Convert the stored device id string into an unsigned integer 61 // corresponding to the selected device. 62 UINT device_id = WAVE_MAPPER; 63 if (!GetDeviceId(&device_id)) { 64 return false; 65 } 66 67 // Open the specified input device for recording. 68 MMRESULT result = MMSYSERR_NOERROR; 69 result = ::waveInOpen(&wavein_, device_id, &format_, 70 reinterpret_cast<DWORD_PTR>(WaveCallback), 71 reinterpret_cast<DWORD_PTR>(this), 72 CALLBACK_FUNCTION); 73 if (result != MMSYSERR_NOERROR) 74 return false; 75 76 SetupBuffers(); 77 state_ = kStateReady; 78 return true; 79} 80 81void PCMWaveInAudioInputStream::SetupBuffers() { 82 WAVEHDR* last = NULL; 83 WAVEHDR* first = NULL; 84 for (int ix = 0; ix != num_buffers_; ++ix) { 85 uint32 sz = sizeof(WAVEHDR) + buffer_size_; 86 buffer_ = reinterpret_cast<WAVEHDR*>(new char[sz]); 87 buffer_->lpData = reinterpret_cast<char*>(buffer_) + sizeof(WAVEHDR); 88 buffer_->dwBufferLength = buffer_size_; 89 buffer_->dwBytesRecorded = 0; 90 buffer_->dwUser = reinterpret_cast<DWORD_PTR>(last); 91 buffer_->dwFlags = WHDR_DONE; 92 buffer_->dwLoops = 0; 93 if (ix == 0) 94 first = buffer_; 95 last = buffer_; 96 ::waveInPrepareHeader(wavein_, buffer_, sizeof(WAVEHDR)); 97 } 98 // Fix the first buffer to point to the last one. 99 first->dwUser = reinterpret_cast<DWORD_PTR>(last); 100} 101 102void PCMWaveInAudioInputStream::FreeBuffers() { 103 WAVEHDR* current = buffer_; 104 for (int ix = 0; ix != num_buffers_; ++ix) { 105 WAVEHDR* next = GetNextBuffer(current); 106 if (current->dwFlags & WHDR_PREPARED) 107 ::waveInUnprepareHeader(wavein_, current, sizeof(WAVEHDR)); 108 delete[] reinterpret_cast<char*>(current); 109 current = next; 110 } 111 buffer_ = NULL; 112} 113 114void PCMWaveInAudioInputStream::Start(AudioInputCallback* callback) { 115 DCHECK(thread_checker_.CalledOnValidThread()); 116 if (state_ != kStateReady) 117 return; 118 119 DCHECK(!callback_); 120 callback_ = callback; 121 state_ = kStateRecording; 122 123 WAVEHDR* buffer = buffer_; 124 for (int ix = 0; ix != num_buffers_; ++ix) { 125 QueueNextPacket(buffer); 126 buffer = GetNextBuffer(buffer); 127 } 128 buffer = buffer_; 129 130 MMRESULT result = ::waveInStart(wavein_); 131 if (result != MMSYSERR_NOERROR) { 132 HandleError(result); 133 state_ = kStateReady; 134 callback_ = NULL; 135 } 136} 137 138// Stopping is tricky. First, no buffer should be locked by the audio driver 139// or else the waveInReset() will deadlock and secondly, the callback should 140// not be inside the AudioInputCallback's OnData because waveInReset() 141// forcefully kills the callback thread. 142void PCMWaveInAudioInputStream::Stop() { 143 DVLOG(1) << "PCMWaveInAudioInputStream::Stop()"; 144 DCHECK(thread_checker_.CalledOnValidThread()); 145 if (state_ != kStateRecording) 146 return; 147 148 bool already_stopped = false; 149 { 150 // Tell the callback that we're stopping. 151 // As a result, |stopped_event_| will be signaled in callback method. 152 base::AutoLock auto_lock(lock_); 153 already_stopped = (callback_ == NULL); 154 callback_ = NULL; 155 } 156 157 if (already_stopped) 158 return; 159 160 // Wait for the callback to finish, it will signal us when ready to be reset. 161 DWORD wait = ::WaitForSingleObject(stopped_event_, INFINITE); 162 DCHECK_EQ(wait, WAIT_OBJECT_0); 163 164 // Stop input and reset the current position to zero for |wavein_|. 165 // All pending buffers are marked as done and returned to the application. 166 MMRESULT res = ::waveInReset(wavein_); 167 DCHECK_EQ(res, static_cast<MMRESULT>(MMSYSERR_NOERROR)); 168 169 state_ = kStateReady; 170} 171 172void PCMWaveInAudioInputStream::Close() { 173 DVLOG(1) << "PCMWaveInAudioInputStream::Close()"; 174 DCHECK(thread_checker_.CalledOnValidThread()); 175 176 // We should not call Close() while recording. Catch it with DCHECK and 177 // implement auto-stop just in case. 178 DCHECK_NE(state_, kStateRecording); 179 Stop(); 180 181 if (wavein_) { 182 FreeBuffers(); 183 184 // waveInClose() generates a WIM_CLOSE callback. In case Start() was never 185 // called, force a reset to ensure close succeeds. 186 MMRESULT res = ::waveInReset(wavein_); 187 DCHECK_EQ(res, static_cast<MMRESULT>(MMSYSERR_NOERROR)); 188 res = ::waveInClose(wavein_); 189 DCHECK_EQ(res, static_cast<MMRESULT>(MMSYSERR_NOERROR)); 190 state_ = kStateClosed; 191 wavein_ = NULL; 192 } 193 194 // Tell the audio manager that we have been released. This can result in 195 // the manager destroying us in-place so this needs to be the last thing 196 // we do on this function. 197 manager_->ReleaseInputStream(this); 198} 199 200double PCMWaveInAudioInputStream::GetMaxVolume() { 201 // TODO(henrika): Add volume support using the Audio Mixer API. 202 return 0.0; 203} 204 205void PCMWaveInAudioInputStream::SetVolume(double volume) { 206 // TODO(henrika): Add volume support using the Audio Mixer API. 207} 208 209double PCMWaveInAudioInputStream::GetVolume() { 210 // TODO(henrika): Add volume support using the Audio Mixer API. 211 return 0.0; 212} 213 214void PCMWaveInAudioInputStream::SetAutomaticGainControl(bool enabled) { 215 // TODO(henrika): Add AGC support when volume control has been added. 216 NOTIMPLEMENTED(); 217} 218 219bool PCMWaveInAudioInputStream::GetAutomaticGainControl() { 220 // TODO(henrika): Add AGC support when volume control has been added. 221 NOTIMPLEMENTED(); 222 return false; 223} 224 225void PCMWaveInAudioInputStream::HandleError(MMRESULT error) { 226 DLOG(WARNING) << "PCMWaveInAudio error " << error; 227 if (callback_) 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