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