wavein_input_win.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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 { 16const int kStopInputStreamCallbackTimeout = 3000; // Three seconds. 17} 18 19namespace media { 20 21// Our sound buffers are allocated once and kept in a linked list using the 22// the WAVEHDR::dwUser variable. The last buffer points to the first buffer. 23static WAVEHDR* GetNextBuffer(WAVEHDR* current) { 24 return reinterpret_cast<WAVEHDR*>(current->dwUser); 25} 26 27PCMWaveInAudioInputStream::PCMWaveInAudioInputStream( 28 AudioManagerWin* manager, const AudioParameters& params, int num_buffers, 29 const std::string& device_id) 30 : state_(kStateEmpty), 31 manager_(manager), 32 device_id_(device_id), 33 wavein_(NULL), 34 callback_(NULL), 35 num_buffers_(num_buffers), 36 buffer_(NULL), 37 channels_(params.channels()) { 38 DCHECK_GT(num_buffers_, 0); 39 format_.wFormatTag = WAVE_FORMAT_PCM; 40 format_.nChannels = params.channels() > 2 ? 2 : params.channels(); 41 format_.nSamplesPerSec = params.sample_rate(); 42 format_.wBitsPerSample = params.bits_per_sample(); 43 format_.cbSize = 0; 44 format_.nBlockAlign = (format_.nChannels * format_.wBitsPerSample) / 8; 45 format_.nAvgBytesPerSec = format_.nBlockAlign * format_.nSamplesPerSec; 46 buffer_size_ = params.frames_per_buffer() * format_.nBlockAlign; 47 // If we don't have a packet size we use 100ms. 48 if (!buffer_size_) 49 buffer_size_ = format_.nAvgBytesPerSec / 10; 50 // The event is auto-reset. 51 stopped_event_.Set(::CreateEventW(NULL, FALSE, FALSE, NULL)); 52} 53 54PCMWaveInAudioInputStream::~PCMWaveInAudioInputStream() { 55 DCHECK(NULL == wavein_); 56} 57 58bool PCMWaveInAudioInputStream::Open() { 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 if (state_ != kStateReady) 120 return; 121 122 callback_ = callback; 123 state_ = kStateRecording; 124 125 WAVEHDR* buffer = buffer_; 126 for (int ix = 0; ix != num_buffers_; ++ix) { 127 QueueNextPacket(buffer); 128 buffer = GetNextBuffer(buffer); 129 } 130 buffer = buffer_; 131 132 MMRESULT result = ::waveInStart(wavein_); 133 if (result != MMSYSERR_NOERROR) { 134 HandleError(result); 135 state_ = kStateReady; 136 } else { 137 manager_->IncreaseActiveInputStreamCount(); 138 } 139} 140 141// Stopping is tricky. First, no buffer should be locked by the audio driver 142// or else the waveInReset() will deadlock and secondly, the callback should 143// not be inside the AudioInputCallback's OnData because waveInReset() 144// forcefully kills the callback thread. 145void PCMWaveInAudioInputStream::Stop() { 146 if (state_ != kStateRecording) 147 return; 148 state_ = kStateStopping; 149 // Wait for the callback to finish, it will signal us when ready to be reset. 150 if (WAIT_OBJECT_0 != 151 ::WaitForSingleObject(stopped_event_, kStopInputStreamCallbackTimeout)) { 152 HandleError(::GetLastError()); 153 return; 154 } 155 // Stop is always called before Close. In case of error, this will be 156 // also called when closing the input controller. 157 manager_->DecreaseActiveInputStreamCount(); 158 159 state_ = kStateStopped; 160 MMRESULT res = ::waveInReset(wavein_); 161 if (res != MMSYSERR_NOERROR) { 162 state_ = kStateRecording; 163 HandleError(res); 164 return; 165 } 166 state_ = kStateReady; 167} 168 169// We can Close in any state except that when trying to close a stream that is 170// recording Windows generates an error, which we propagate to the source. 171void PCMWaveInAudioInputStream::Close() { 172 if (wavein_) { 173 // waveInClose generates a callback with WIM_CLOSE id in the same thread. 174 MMRESULT res = ::waveInClose(wavein_); 175 if (res != MMSYSERR_NOERROR) { 176 HandleError(res); 177 return; 178 } 179 state_ = kStateClosed; 180 wavein_ = NULL; 181 FreeBuffers(); 182 } 183 // Tell the audio manager that we have been released. This can result in 184 // the manager destroying us in-place so this needs to be the last thing 185 // we do on this function. 186 manager_->ReleaseInputStream(this); 187} 188 189double PCMWaveInAudioInputStream::GetMaxVolume() { 190 // TODO(henrika): Add volume support using the Audio Mixer API. 191 return 0.0; 192} 193 194void PCMWaveInAudioInputStream::SetVolume(double volume) { 195 // TODO(henrika): Add volume support using the Audio Mixer API. 196} 197 198double PCMWaveInAudioInputStream::GetVolume() { 199 // TODO(henrika): Add volume support using the Audio Mixer API. 200 return 0.0; 201} 202 203void PCMWaveInAudioInputStream::SetAutomaticGainControl(bool enabled) { 204 // TODO(henrika): Add AGC support when volume control has been added. 205 NOTIMPLEMENTED(); 206} 207 208bool PCMWaveInAudioInputStream::GetAutomaticGainControl() { 209 // TODO(henrika): Add AGC support when volume control has been added. 210 NOTIMPLEMENTED(); 211 return false; 212} 213 214void PCMWaveInAudioInputStream::HandleError(MMRESULT error) { 215 DLOG(WARNING) << "PCMWaveInAudio error " << error; 216 callback_->OnError(this, error); 217} 218 219void PCMWaveInAudioInputStream::QueueNextPacket(WAVEHDR *buffer) { 220 MMRESULT res = ::waveInAddBuffer(wavein_, buffer, sizeof(WAVEHDR)); 221 if (res != MMSYSERR_NOERROR) 222 HandleError(res); 223} 224 225bool PCMWaveInAudioInputStream::GetDeviceId(UINT* device_index) { 226 // Deliver the default input device id (WAVE_MAPPER) if the default 227 // device has been selected. 228 if (device_id_ == AudioManagerBase::kDefaultDeviceId) { 229 *device_index = WAVE_MAPPER; 230 return true; 231 } 232 233 // Get list of all available and active devices. 234 AudioDeviceNames device_names; 235 if (!media::GetInputDeviceNamesWinXP(&device_names)) 236 return false; 237 238 if (device_names.empty()) 239 return false; 240 241 // Search the full list of devices and compare with the specified 242 // device id which was specified in the constructor. Stop comparing 243 // when a match is found and return the corresponding index. 244 UINT index = 0; 245 bool found_device = false; 246 AudioDeviceNames::const_iterator it = device_names.begin(); 247 while (it != device_names.end()) { 248 if (it->unique_id.compare(device_id_) == 0) { 249 *device_index = index; 250 found_device = true; 251 break; 252 } 253 ++index; 254 ++it; 255 } 256 257 return found_device; 258} 259 260// Windows calls us back in this function when some events happen. Most notably 261// when it has an audio buffer with recorded data. 262void PCMWaveInAudioInputStream::WaveCallback(HWAVEIN hwi, UINT msg, 263 DWORD_PTR instance, 264 DWORD_PTR param1, DWORD_PTR) { 265 PCMWaveInAudioInputStream* obj = 266 reinterpret_cast<PCMWaveInAudioInputStream*>(instance); 267 268 if (msg == WIM_DATA) { 269 // WIM_DONE indicates that the driver is done with our buffer. We pass it 270 // to the callback and check if we need to stop playing. 271 // It should be OK to assume the data in the buffer is what has been 272 // recorded in the soundcard. 273 // TODO(henrika): the |volume| parameter is always set to zero since there 274 // is currently no support for controlling the microphone volume level. 275 WAVEHDR* buffer = reinterpret_cast<WAVEHDR*>(param1); 276 obj->callback_->OnData(obj, reinterpret_cast<const uint8*>(buffer->lpData), 277 buffer->dwBytesRecorded, 278 buffer->dwBytesRecorded, 279 0.0); 280 281 if (obj->state_ == kStateStopping) { 282 // The main thread has called Stop() and is waiting to issue waveOutReset 283 // which will kill this thread. We should not enter AudioSourceCallback 284 // code anymore. 285 ::SetEvent(obj->stopped_event_); 286 } else if (obj->state_ == kStateStopped) { 287 // Not sure if ever hit this but just in case. 288 } else { 289 // Queue the finished buffer back with the audio driver. Since we are 290 // reusing the same buffers we can get away without calling 291 // waveInPrepareHeader. 292 obj->QueueNextPacket(buffer); 293 } 294 } else if (msg == WIM_CLOSE) { 295 // We can be closed before calling Start, so it is possible to have a 296 // null callback at this point. 297 if (obj->callback_) 298 obj->callback_->OnClose(obj); 299 } 300} 301 302} // namespace media 303