Upstream version 11.39.250.0
[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 #include "media/base/audio_bus.h"
14
15 namespace 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.
19 static WAVEHDR* GetNextBuffer(WAVEHDR* current) {
20   return reinterpret_cast<WAVEHDR*>(current->dwUser);
21 }
22
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       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
53 PCMWaveInAudioInputStream::~PCMWaveInAudioInputStream() {
54   DCHECK(NULL == wavein_);
55 }
56
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;
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
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));
101   }
102   // Fix the first buffer to point to the last one.
103   first->dwUser = reinterpret_cast<DWORD_PTR>(last);
104 }
105
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;
114   }
115   buffer_ = NULL;
116 }
117
118 void 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.
146 void 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
176 void 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
204 double PCMWaveInAudioInputStream::GetMaxVolume() {
205   // TODO(henrika): Add volume support using the Audio Mixer API.
206   return 0.0;
207 }
208
209 void PCMWaveInAudioInputStream::SetVolume(double volume) {
210   // TODO(henrika): Add volume support using the Audio Mixer API.
211 }
212
213 double PCMWaveInAudioInputStream::GetVolume() {
214   // TODO(henrika): Add volume support using the Audio Mixer API.
215   return 0.0;
216 }
217
218 void PCMWaveInAudioInputStream::SetAutomaticGainControl(bool enabled) {
219   // TODO(henrika): Add AGC support when volume control has been added.
220   NOTIMPLEMENTED();
221 }
222
223 bool PCMWaveInAudioInputStream::GetAutomaticGainControl() {
224   // TODO(henrika): Add AGC support when volume control has been added.
225   NOTIMPLEMENTED();
226   return false;
227 }
228
229 bool PCMWaveInAudioInputStream::IsMuted() {
230   NOTIMPLEMENTED();
231   return false;
232 }
233
234 void PCMWaveInAudioInputStream::HandleError(MMRESULT error) {
235   DLOG(WARNING) << "PCMWaveInAudio error " << error;
236   if (callback_)
237     callback_->OnError(this);
238 }
239
240 void PCMWaveInAudioInputStream::QueueNextPacket(WAVEHDR *buffer) {
241   MMRESULT res = ::waveInAddBuffer(wavein_, buffer, sizeof(WAVEHDR));
242   if (res != MMSYSERR_NOERROR)
243     HandleError(res);
244 }
245
246 bool 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.
283 void 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