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