cc: Added inline to Tile::IsReadyToDraw
[chromium-blink-merge.git] / media / audio / win / wavein_input_win.cc
blob3c4147738df846928677b4e7c91bfb936142b3bc
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/audio_util.h"
12 #include "media/audio/win/audio_manager_win.h"
13 #include "media/audio/win/device_enumeration_win.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, 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));
50 PCMWaveInAudioInputStream::~PCMWaveInAudioInputStream() {
51 DCHECK(NULL == wavein_);
54 bool 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;
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;
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;
77 SetupBuffers();
78 state_ = kStateReady;
79 return true;
82 void 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));
99 // Fix the first buffer to point to the last one.
100 first->dwUser = reinterpret_cast<DWORD_PTR>(last);
103 void 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;
112 buffer_ = NULL;
115 void PCMWaveInAudioInputStream::Start(AudioInputCallback* callback) {
116 DCHECK(thread_checker_.CalledOnValidThread());
117 if (state_ != kStateReady)
118 return;
120 DCHECK(!callback_);
121 callback_ = callback;
122 state_ = kStateRecording;
124 WAVEHDR* buffer = buffer_;
125 for (int ix = 0; ix != num_buffers_; ++ix) {
126 QueueNextPacket(buffer);
127 buffer = GetNextBuffer(buffer);
129 buffer = buffer_;
131 MMRESULT result = ::waveInStart(wavein_);
132 if (result != MMSYSERR_NOERROR) {
133 HandleError(result);
134 state_ = kStateReady;
135 callback_ = NULL;
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.
143 void PCMWaveInAudioInputStream::Stop() {
144 DVLOG(1) << "PCMWaveInAudioInputStream::Stop()";
145 DCHECK(thread_checker_.CalledOnValidThread());
146 if (state_ != kStateRecording)
147 return;
149 bool already_stopped = false;
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;
158 if (already_stopped)
159 return;
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);
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));
170 state_ = kStateReady;
173 void PCMWaveInAudioInputStream::Close() {
174 DVLOG(1) << "PCMWaveInAudioInputStream::Close()";
175 DCHECK(thread_checker_.CalledOnValidThread());
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();
182 if (wavein_) {
183 FreeBuffers();
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;
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);
201 double PCMWaveInAudioInputStream::GetMaxVolume() {
202 // TODO(henrika): Add volume support using the Audio Mixer API.
203 return 0.0;
206 void PCMWaveInAudioInputStream::SetVolume(double volume) {
207 // TODO(henrika): Add volume support using the Audio Mixer API.
210 double PCMWaveInAudioInputStream::GetVolume() {
211 // TODO(henrika): Add volume support using the Audio Mixer API.
212 return 0.0;
215 void PCMWaveInAudioInputStream::SetAutomaticGainControl(bool enabled) {
216 // TODO(henrika): Add AGC support when volume control has been added.
217 NOTIMPLEMENTED();
220 bool PCMWaveInAudioInputStream::GetAutomaticGainControl() {
221 // TODO(henrika): Add AGC support when volume control has been added.
222 NOTIMPLEMENTED();
223 return false;
226 void PCMWaveInAudioInputStream::HandleError(MMRESULT error) {
227 DLOG(WARNING) << "PCMWaveInAudio error " << error;
228 callback_->OnError(this);
231 void PCMWaveInAudioInputStream::QueueNextPacket(WAVEHDR *buffer) {
232 MMRESULT res = ::waveInAddBuffer(wavein_, buffer, sizeof(WAVEHDR));
233 if (res != MMSYSERR_NOERROR)
234 HandleError(res);
237 bool 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;
245 // Get list of all available and active devices.
246 AudioDeviceNames device_names;
247 if (!media::GetInputDeviceNamesWinXP(&device_names))
248 return false;
250 if (device_names.empty())
251 return false;
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;
265 ++index;
266 ++it;
269 return found_device;
272 // Windows calls us back in this function when some events happen. Most notably
273 // when it has an audio buffer with recorded data.
274 void PCMWaveInAudioInputStream::WaveCallback(HWAVEIN hwi, UINT msg,
275 DWORD_PTR instance,
276 DWORD_PTR param1, DWORD_PTR) {
277 PCMWaveInAudioInputStream* obj =
278 reinterpret_cast<PCMWaveInAudioInputStream*>(instance);
280 // The lock ensures that Stop() can't be called during a callback.
281 base::AutoLock auto_lock(obj->lock_);
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);
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_);
309 } else if (msg == WIM_CLOSE) {
310 // Intentionaly no-op for now.
311 } else if (msg == WIM_OPEN) {
312 // Intentionaly no-op for now.
316 } // namespace media