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