- add sources.
[platform/framework/web/crosswalk.git] / src / media / audio / win / wavein_input_win.cc
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
14 namespace media {
15
16 // Our sound buffers are allocated once and kept in a linked list using the
17 // the WAVEHDR::dwUser variable. The last buffer points to the first buffer.
18 static WAVEHDR* GetNextBuffer(WAVEHDR* current) {
19   return reinterpret_cast<WAVEHDR*>(current->dwUser);
20 }
21
22 PCMWaveInAudioInputStream::PCMWaveInAudioInputStream(
23     AudioManagerWin* manager, const AudioParameters& params, int num_buffers,
24     const std::string& device_id)
25     : state_(kStateEmpty),
26       manager_(manager),
27       device_id_(device_id),
28       wavein_(NULL),
29       callback_(NULL),
30       num_buffers_(num_buffers),
31       buffer_(NULL),
32       channels_(params.channels()) {
33   DCHECK_GT(num_buffers_, 0);
34   format_.wFormatTag = WAVE_FORMAT_PCM;
35   format_.nChannels = params.channels() > 2 ? 2 : params.channels();
36   format_.nSamplesPerSec = params.sample_rate();
37   format_.wBitsPerSample = params.bits_per_sample();
38   format_.cbSize = 0;
39   format_.nBlockAlign = (format_.nChannels * format_.wBitsPerSample) / 8;
40   format_.nAvgBytesPerSec = format_.nBlockAlign * format_.nSamplesPerSec;
41   buffer_size_ = params.frames_per_buffer() * format_.nBlockAlign;
42   // If we don't have a packet size we use 100ms.
43   if (!buffer_size_)
44     buffer_size_ = format_.nAvgBytesPerSec / 10;
45   // The event is auto-reset.
46   stopped_event_.Set(::CreateEventW(NULL, FALSE, FALSE, NULL));
47 }
48
49 PCMWaveInAudioInputStream::~PCMWaveInAudioInputStream() {
50   DCHECK(NULL == wavein_);
51 }
52
53 bool PCMWaveInAudioInputStream::Open() {
54   DCHECK(thread_checker_.CalledOnValidThread());
55   if (state_ != kStateEmpty)
56     return false;
57   if (num_buffers_ < 2 || num_buffers_ > 10)
58     return false;
59
60   // Convert the stored device id string into an unsigned integer
61   // corresponding to the selected device.
62   UINT device_id = WAVE_MAPPER;
63   if (!GetDeviceId(&device_id)) {
64     return false;
65   }
66
67   // Open the specified input device for recording.
68   MMRESULT result = MMSYSERR_NOERROR;
69   result = ::waveInOpen(&wavein_, device_id, &format_,
70                         reinterpret_cast<DWORD_PTR>(WaveCallback),
71                         reinterpret_cast<DWORD_PTR>(this),
72                         CALLBACK_FUNCTION);
73   if (result != MMSYSERR_NOERROR)
74     return false;
75
76   SetupBuffers();
77   state_ = kStateReady;
78   return true;
79 }
80
81 void PCMWaveInAudioInputStream::SetupBuffers() {
82   WAVEHDR* last = NULL;
83   WAVEHDR* first = NULL;
84   for (int ix = 0; ix != num_buffers_; ++ix) {
85     uint32 sz = sizeof(WAVEHDR) + buffer_size_;
86     buffer_ =  reinterpret_cast<WAVEHDR*>(new char[sz]);
87     buffer_->lpData = reinterpret_cast<char*>(buffer_) + sizeof(WAVEHDR);
88     buffer_->dwBufferLength = buffer_size_;
89     buffer_->dwBytesRecorded = 0;
90     buffer_->dwUser = reinterpret_cast<DWORD_PTR>(last);
91     buffer_->dwFlags = WHDR_DONE;
92     buffer_->dwLoops = 0;
93     if (ix == 0)
94       first = buffer_;
95     last = buffer_;
96     ::waveInPrepareHeader(wavein_, buffer_, sizeof(WAVEHDR));
97   }
98   // Fix the first buffer to point to the last one.
99   first->dwUser = reinterpret_cast<DWORD_PTR>(last);
100 }
101
102 void PCMWaveInAudioInputStream::FreeBuffers() {
103   WAVEHDR* current = buffer_;
104   for (int ix = 0; ix != num_buffers_; ++ix) {
105     WAVEHDR* next = GetNextBuffer(current);
106     if (current->dwFlags & WHDR_PREPARED)
107       ::waveInUnprepareHeader(wavein_, current, sizeof(WAVEHDR));
108     delete[] reinterpret_cast<char*>(current);
109     current = next;
110   }
111   buffer_ = NULL;
112 }
113
114 void PCMWaveInAudioInputStream::Start(AudioInputCallback* callback) {
115   DCHECK(thread_checker_.CalledOnValidThread());
116   if (state_ != kStateReady)
117     return;
118
119   DCHECK(!callback_);
120   callback_ = callback;
121   state_ = kStateRecording;
122
123   WAVEHDR* buffer = buffer_;
124   for (int ix = 0; ix != num_buffers_; ++ix) {
125     QueueNextPacket(buffer);
126     buffer = GetNextBuffer(buffer);
127   }
128   buffer = buffer_;
129
130   MMRESULT result = ::waveInStart(wavein_);
131   if (result != MMSYSERR_NOERROR) {
132     HandleError(result);
133     state_ = kStateReady;
134     callback_ = NULL;
135   }
136 }
137
138 // Stopping is tricky. First, no buffer should be locked by the audio driver
139 // or else the waveInReset() will deadlock and secondly, the callback should
140 // not be inside the AudioInputCallback's OnData because waveInReset()
141 // forcefully kills the callback thread.
142 void PCMWaveInAudioInputStream::Stop() {
143   DVLOG(1) << "PCMWaveInAudioInputStream::Stop()";
144   DCHECK(thread_checker_.CalledOnValidThread());
145   if (state_ != kStateRecording)
146     return;
147
148   bool already_stopped = false;
149   {
150     // Tell the callback that we're stopping.
151     // As a result, |stopped_event_| will be signaled in callback method.
152     base::AutoLock auto_lock(lock_);
153     already_stopped = (callback_ == NULL);
154     callback_ = NULL;
155   }
156
157   if (already_stopped)
158     return;
159
160   // Wait for the callback to finish, it will signal us when ready to be reset.
161   DWORD wait = ::WaitForSingleObject(stopped_event_, INFINITE);
162   DCHECK_EQ(wait, WAIT_OBJECT_0);
163
164   // Stop input and reset the current position to zero for |wavein_|.
165   // All pending buffers are marked as done and returned to the application.
166   MMRESULT res = ::waveInReset(wavein_);
167   DCHECK_EQ(res, static_cast<MMRESULT>(MMSYSERR_NOERROR));
168
169   state_ = kStateReady;
170 }
171
172 void PCMWaveInAudioInputStream::Close() {
173   DVLOG(1) << "PCMWaveInAudioInputStream::Close()";
174   DCHECK(thread_checker_.CalledOnValidThread());
175
176   // We should not call Close() while recording. Catch it with DCHECK and
177   // implement auto-stop just in case.
178   DCHECK_NE(state_, kStateRecording);
179   Stop();
180
181   if (wavein_) {
182     FreeBuffers();
183
184     // waveInClose() generates a WIM_CLOSE callback.  In case Start() was never
185     // called, force a reset to ensure close succeeds.
186     MMRESULT res = ::waveInReset(wavein_);
187     DCHECK_EQ(res, static_cast<MMRESULT>(MMSYSERR_NOERROR));
188     res = ::waveInClose(wavein_);
189     DCHECK_EQ(res, static_cast<MMRESULT>(MMSYSERR_NOERROR));
190     state_ = kStateClosed;
191     wavein_ = NULL;
192   }
193
194   // Tell the audio manager that we have been released. This can result in
195   // the manager destroying us in-place so this needs to be the last thing
196   // we do on this function.
197   manager_->ReleaseInputStream(this);
198 }
199
200 double PCMWaveInAudioInputStream::GetMaxVolume() {
201   // TODO(henrika): Add volume support using the Audio Mixer API.
202   return 0.0;
203 }
204
205 void PCMWaveInAudioInputStream::SetVolume(double volume) {
206   // TODO(henrika): Add volume support using the Audio Mixer API.
207 }
208
209 double PCMWaveInAudioInputStream::GetVolume() {
210   // TODO(henrika): Add volume support using the Audio Mixer API.
211   return 0.0;
212 }
213
214 void PCMWaveInAudioInputStream::SetAutomaticGainControl(bool enabled) {
215   // TODO(henrika): Add AGC support when volume control has been added.
216   NOTIMPLEMENTED();
217 }
218
219 bool PCMWaveInAudioInputStream::GetAutomaticGainControl() {
220   // TODO(henrika): Add AGC support when volume control has been added.
221   NOTIMPLEMENTED();
222   return false;
223 }
224
225 void PCMWaveInAudioInputStream::HandleError(MMRESULT error) {
226   DLOG(WARNING) << "PCMWaveInAudio error " << error;
227   callback_->OnError(this);
228 }
229
230 void PCMWaveInAudioInputStream::QueueNextPacket(WAVEHDR *buffer) {
231   MMRESULT res = ::waveInAddBuffer(wavein_, buffer, sizeof(WAVEHDR));
232   if (res != MMSYSERR_NOERROR)
233     HandleError(res);
234 }
235
236 bool PCMWaveInAudioInputStream::GetDeviceId(UINT* device_index) {
237   // Deliver the default input device id (WAVE_MAPPER) if the default
238   // device has been selected.
239   if (device_id_ == AudioManagerBase::kDefaultDeviceId) {
240     *device_index = WAVE_MAPPER;
241     return true;
242   }
243
244   // Get list of all available and active devices.
245   AudioDeviceNames device_names;
246   if (!media::GetInputDeviceNamesWinXP(&device_names))
247     return false;
248
249   if (device_names.empty())
250     return false;
251
252   // Search the full list of devices and compare with the specified
253   // device id which was specified in the constructor. Stop comparing
254   // when a match is found and return the corresponding index.
255   UINT index = 0;
256   bool found_device = false;
257   AudioDeviceNames::const_iterator it = device_names.begin();
258   while (it != device_names.end()) {
259     if (it->unique_id.compare(device_id_) == 0) {
260       *device_index = index;
261       found_device = true;
262       break;
263     }
264     ++index;
265     ++it;
266   }
267
268   return found_device;
269 }
270
271 // Windows calls us back in this function when some events happen. Most notably
272 // when it has an audio buffer with recorded data.
273 void PCMWaveInAudioInputStream::WaveCallback(HWAVEIN hwi, UINT msg,
274                                              DWORD_PTR instance,
275                                              DWORD_PTR param1, DWORD_PTR) {
276   PCMWaveInAudioInputStream* obj =
277       reinterpret_cast<PCMWaveInAudioInputStream*>(instance);
278
279   // The lock ensures that Stop() can't be called during a callback.
280   base::AutoLock auto_lock(obj->lock_);
281
282   if (msg == WIM_DATA) {
283     // The WIM_DATA message is sent when waveform-audio data is present in
284     // the input buffer and the buffer is being returned to the application.
285     // The message can be sent when the buffer is full or after the
286     // waveInReset function is called.
287     if (obj->callback_) {
288       // TODO(henrika): the |volume| parameter is always set to zero since
289       // there is currently no support for controlling the microphone volume
290       // level.
291       WAVEHDR* buffer = reinterpret_cast<WAVEHDR*>(param1);
292       obj->callback_->OnData(obj,
293                              reinterpret_cast<const uint8*>(buffer->lpData),
294                              buffer->dwBytesRecorded,
295                              buffer->dwBytesRecorded,
296                              0.0);
297
298       // Queue the finished buffer back with the audio driver. Since we are
299       // reusing the same buffers we can get away without calling
300       // waveInPrepareHeader.
301       obj->QueueNextPacket(buffer);
302     } else {
303       // Main thread has called Stop() and set |callback_| to NULL and is
304       // now waiting to issue waveInReset which will kill this thread.
305       // We should not call AudioSourceCallback code anymore.
306       ::SetEvent(obj->stopped_event_);
307     }
308   } else if (msg == WIM_CLOSE) {
309     // Intentionaly no-op for now.
310   } else if (msg == WIM_OPEN) {
311     // Intentionaly no-op for now.
312   }
313 }
314
315 }  // namespace media