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"
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
,
27 const std::string
& device_id
)
28 : state_(kStateEmpty
),
31 num_buffers_(num_buffers
),
32 channels_(params
.channels()),
33 device_id_(device_id
),
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();
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.
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
)
61 if (num_buffers_
< 2 || num_buffers_
> 10)
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
)) {
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),
77 if (result
!= MMSYSERR_NOERROR
)
85 void PCMWaveInAudioInputStream::SetupBuffers() {
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
;
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
);
118 void PCMWaveInAudioInputStream::Start(AudioInputCallback
* callback
) {
119 DCHECK(thread_checker_
.CalledOnValidThread());
120 if (state_
!= kStateReady
)
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
);
134 MMRESULT result
= ::waveInStart(wavein_
);
135 if (result
!= MMSYSERR_NOERROR
) {
137 state_
= kStateReady
;
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
)
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
);
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
);
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
;
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.
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.
218 bool PCMWaveInAudioInputStream::SetAutomaticGainControl(bool enabled
) {
219 // TODO(henrika): Add AGC support when volume control has been added.
224 bool PCMWaveInAudioInputStream::GetAutomaticGainControl() {
225 // TODO(henrika): Add AGC support when volume control has been added.
230 bool PCMWaveInAudioInputStream::IsMuted() {
235 void PCMWaveInAudioInputStream::HandleError(MMRESULT error
) {
236 DLOG(WARNING
) << "PCMWaveInAudio error " << error
;
238 callback_
->OnError(this);
241 void PCMWaveInAudioInputStream::QueueNextPacket(WAVEHDR
*buffer
) {
242 MMRESULT res
= ::waveInAddBuffer(wavein_
, buffer
, sizeof(WAVEHDR
));
243 if (res
!= MMSYSERR_NOERROR
)
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
;
255 // Get list of all available and active devices.
256 AudioDeviceNames device_names
;
257 if (!media::GetInputDeviceNamesWinXP(&device_names
))
260 if (device_names
.empty())
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.
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
;
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
,
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
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
);
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.