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
229bool PCMWaveInAudioInputStream::IsMuted() {
230  NOTIMPLEMENTED();
231  return false;
232}
233
234void PCMWaveInAudioInputStream::HandleError(MMRESULT error) {
235  DLOG(WARNING) << "PCMWaveInAudio error " << error;
236  if (callback_)
237    callback_->OnError(this);
238}
239
240void PCMWaveInAudioInputStream::QueueNextPacket(WAVEHDR *buffer) {
241  MMRESULT res = ::waveInAddBuffer(wavein_, buffer, sizeof(WAVEHDR));
242  if (res != MMSYSERR_NOERROR)
243    HandleError(res);
244}
245
246bool PCMWaveInAudioInputStream::GetDeviceId(UINT* device_index) {
247  // Deliver the default input device id (WAVE_MAPPER) if the default
248  // device has been selected.
249  if (device_id_ == AudioManagerBase::kDefaultDeviceId) {
250    *device_index = WAVE_MAPPER;
251    return true;
252  }
253
254  // Get list of all available and active devices.
255  AudioDeviceNames device_names;
256  if (!media::GetInputDeviceNamesWinXP(&device_names))
257    return false;
258
259  if (device_names.empty())
260    return false;
261
262  // Search the full list of devices and compare with the specified
263  // device id which was specified in the constructor. Stop comparing
264  // when a match is found and return the corresponding index.
265  UINT index = 0;
266  bool found_device = false;
267  AudioDeviceNames::const_iterator it = device_names.begin();
268  while (it != device_names.end()) {
269    if (it->unique_id.compare(device_id_) == 0) {
270      *device_index = index;
271      found_device = true;
272      break;
273    }
274    ++index;
275    ++it;
276  }
277
278  return found_device;
279}
280
281// Windows calls us back in this function when some events happen. Most notably
282// when it has an audio buffer with recorded data.
283void PCMWaveInAudioInputStream::WaveCallback(HWAVEIN hwi, UINT msg,
284                                             DWORD_PTR instance,
285                                             DWORD_PTR param1, DWORD_PTR) {
286  PCMWaveInAudioInputStream* obj =
287      reinterpret_cast<PCMWaveInAudioInputStream*>(instance);
288
289  // The lock ensures that Stop() can't be called during a callback.
290  base::AutoLock auto_lock(obj->lock_);
291
292  if (msg == WIM_DATA) {
293    // The WIM_DATA message is sent when waveform-audio data is present in
294    // the input buffer and the buffer is being returned to the application.
295    // The message can be sent when the buffer is full or after the
296    // waveInReset function is called.
297    if (obj->callback_) {
298      // TODO(henrika): the |volume| parameter is always set to zero since
299      // there is currently no support for controlling the microphone volume
300      // level.
301      WAVEHDR* buffer = reinterpret_cast<WAVEHDR*>(param1);
302      obj->audio_bus_->FromInterleaved(reinterpret_cast<uint8*>(buffer->lpData),
303                                       obj->audio_bus_->frames(),
304                                       obj->format_.wBitsPerSample / 8);
305      obj->callback_->OnData(
306          obj, obj->audio_bus_.get(), buffer->dwBytesRecorded, 0.0);
307
308      // Queue the finished buffer back with the audio driver. Since we are
309      // reusing the same buffers we can get away without calling
310      // waveInPrepareHeader.
311      obj->QueueNextPacket(buffer);
312    } else {
313      // Main thread has called Stop() and set |callback_| to NULL and is
314      // now waiting to issue waveInReset which will kill this thread.
315      // We should not call AudioSourceCallback code anymore.
316      ::SetEvent(obj->stopped_event_.Get());
317    }
318  } else if (msg == WIM_CLOSE) {
319    // Intentionaly no-op for now.
320  } else if (msg == WIM_OPEN) {
321    // Intentionaly no-op for now.
322  }
323}
324
325}  // namespace media
326