Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / media / audio / win / wavein_input_win.cc
blobd93d62d3a93c44eb07cbc9294c7f5216b5ddba65
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.
5 #include "media/audio/win/wavein_input_win.h"
7 #pragma comment(lib, "winmm.lib")
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"
15 namespace media {
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.
19 static WAVEHDR* GetNextBuffer(WAVEHDR* current) {
20 return reinterpret_cast<WAVEHDR*>(current->dwUser);
23 PCMWaveInAudioInputStream::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 callback_(NULL),
31 num_buffers_(num_buffers),
32 channels_(params.channels()),
33 device_id_(device_id),
34 wavein_(NULL),
35 buffer_(NULL),
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));
53 PCMWaveInAudioInputStream::~PCMWaveInAudioInputStream() {
54 DCHECK(NULL == wavein_);
57 bool 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;
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;
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;
80 SetupBuffers();
81 state_ = kStateReady;
82 return true;
85 void 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));
102 // Fix the first buffer to point to the last one.
103 first->dwUser = reinterpret_cast<DWORD_PTR>(last);
106 void 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;
115 buffer_ = NULL;
118 void PCMWaveInAudioInputStream::Start(AudioInputCallback* callback) {
119 DCHECK(thread_checker_.CalledOnValidThread());
120 if (state_ != kStateReady)
121 return;
123 DCHECK(!callback_);
124 callback_ = callback;
125 state_ = kStateRecording;
127 WAVEHDR* buffer = buffer_;
128 for (int ix = 0; ix != num_buffers_; ++ix) {
129 QueueNextPacket(buffer);
130 buffer = GetNextBuffer(buffer);
132 buffer = buffer_;
134 MMRESULT result = ::waveInStart(wavein_);
135 if (result != MMSYSERR_NOERROR) {
136 HandleError(result);
137 state_ = kStateReady;
138 callback_ = NULL;
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.
146 void PCMWaveInAudioInputStream::Stop() {
147 DVLOG(1) << "PCMWaveInAudioInputStream::Stop()";
148 DCHECK(thread_checker_.CalledOnValidThread());
149 if (state_ != kStateRecording)
150 return;
152 bool already_stopped = false;
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;
161 if (already_stopped)
162 return;
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);
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));
173 state_ = kStateReady;
176 void PCMWaveInAudioInputStream::Close() {
177 DVLOG(1) << "PCMWaveInAudioInputStream::Close()";
178 DCHECK(thread_checker_.CalledOnValidThread());
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();
185 if (wavein_) {
186 FreeBuffers();
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;
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);
204 double PCMWaveInAudioInputStream::GetMaxVolume() {
205 // TODO(henrika): Add volume support using the Audio Mixer API.
206 return 0.0;
209 void PCMWaveInAudioInputStream::SetVolume(double volume) {
210 // TODO(henrika): Add volume support using the Audio Mixer API.
213 double PCMWaveInAudioInputStream::GetVolume() {
214 // TODO(henrika): Add volume support using the Audio Mixer API.
215 return 0.0;
218 bool PCMWaveInAudioInputStream::SetAutomaticGainControl(bool enabled) {
219 // TODO(henrika): Add AGC support when volume control has been added.
220 NOTIMPLEMENTED();
221 return false;
224 bool PCMWaveInAudioInputStream::GetAutomaticGainControl() {
225 // TODO(henrika): Add AGC support when volume control has been added.
226 NOTIMPLEMENTED();
227 return false;
230 bool PCMWaveInAudioInputStream::IsMuted() {
231 NOTIMPLEMENTED();
232 return false;
235 void PCMWaveInAudioInputStream::HandleError(MMRESULT error) {
236 DLOG(WARNING) << "PCMWaveInAudio error " << error;
237 if (callback_)
238 callback_->OnError(this);
241 void PCMWaveInAudioInputStream::QueueNextPacket(WAVEHDR *buffer) {
242 MMRESULT res = ::waveInAddBuffer(wavein_, buffer, sizeof(WAVEHDR));
243 if (res != MMSYSERR_NOERROR)
244 HandleError(res);
247 bool PCMWaveInAudioInputStream::GetDeviceId(UINT* device_index) {
248 // Deliver the default input device id (WAVE_MAPPER) if the default
249 // device has been selected.
250 if (device_id_ == AudioManagerBase::kDefaultDeviceId) {
251 *device_index = WAVE_MAPPER;
252 return true;
255 // Get list of all available and active devices.
256 AudioDeviceNames device_names;
257 if (!media::GetInputDeviceNamesWinXP(&device_names))
258 return false;
260 if (device_names.empty())
261 return false;
263 // Search the full list of devices and compare with the specified
264 // device id which was specified in the constructor. Stop comparing
265 // when a match is found and return the corresponding index.
266 UINT index = 0;
267 bool found_device = false;
268 AudioDeviceNames::const_iterator it = device_names.begin();
269 while (it != device_names.end()) {
270 if (it->unique_id.compare(device_id_) == 0) {
271 *device_index = index;
272 found_device = true;
273 break;
275 ++index;
276 ++it;
279 return found_device;
282 // Windows calls us back in this function when some events happen. Most notably
283 // when it has an audio buffer with recorded data.
284 void PCMWaveInAudioInputStream::WaveCallback(HWAVEIN hwi, UINT msg,
285 DWORD_PTR instance,
286 DWORD_PTR param1, DWORD_PTR) {
287 PCMWaveInAudioInputStream* obj =
288 reinterpret_cast<PCMWaveInAudioInputStream*>(instance);
290 // The lock ensures that Stop() can't be called during a callback.
291 base::AutoLock auto_lock(obj->lock_);
293 if (msg == WIM_DATA) {
294 // The WIM_DATA message is sent when waveform-audio data is present in
295 // the input buffer and the buffer is being returned to the application.
296 // The message can be sent when the buffer is full or after the
297 // waveInReset function is called.
298 if (obj->callback_) {
299 // TODO(henrika): the |volume| parameter is always set to zero since
300 // there is currently no support for controlling the microphone volume
301 // level.
302 WAVEHDR* buffer = reinterpret_cast<WAVEHDR*>(param1);
303 obj->audio_bus_->FromInterleaved(reinterpret_cast<uint8*>(buffer->lpData),
304 obj->audio_bus_->frames(),
305 obj->format_.wBitsPerSample / 8);
306 obj->callback_->OnData(
307 obj, obj->audio_bus_.get(), buffer->dwBytesRecorded, 0.0);
309 // Queue the finished buffer back with the audio driver. Since we are
310 // reusing the same buffers we can get away without calling
311 // waveInPrepareHeader.
312 obj->QueueNextPacket(buffer);
313 } else {
314 // Main thread has called Stop() and set |callback_| to NULL and is
315 // now waiting to issue waveInReset which will kill this thread.
316 // We should not call AudioSourceCallback code anymore.
317 ::SetEvent(obj->stopped_event_.Get());
319 } else if (msg == WIM_CLOSE) {
320 // Intentionaly no-op for now.
321 } else if (msg == WIM_OPEN) {
322 // Intentionaly no-op for now.
326 } // namespace media