Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(325)

Side by Side Diff: media/audio/win/wavein_input_win.cc

Issue 2966005: Add recording capability to AudioManager, and implemented on windows using the WaveIn APIs. (Closed)
Patch Set: Patch Created 10 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « media/audio/win/wavein_input_win.h ('k') | media/media.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2010 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 #include <windows.h>
8 #include <mmsystem.h>
9 #pragma comment(lib, "winmm.lib")
10
11 #include "base/basictypes.h"
12 #include "base/logging.h"
13 #include "media/audio/audio_io.h"
14 #include "media/audio/audio_util.h"
15 #include "media/audio/win/audio_manager_win.h"
16
17 namespace {
18
19 // Our sound buffers are allocated once and kept in a linked list using the
20 // the WAVEHDR::dwUser variable. The last buffer points to the first buffer.
21 WAVEHDR* GetNextBuffer(WAVEHDR* current) {
22 return reinterpret_cast<WAVEHDR*>(current->dwUser);
23 }
24
25 } // namespace
26
27 PCMWaveInAudioInputStream::PCMWaveInAudioInputStream(
28 AudioManagerWin* manager, int channels, int sampling_rate, int num_buffers,
29 char bits_per_sample, uint32 samples_per_packet, UINT device_id)
30 : state_(kStateEmpty),
31 manager_(manager),
32 device_id_(device_id),
33 wavein_(NULL),
34 callback_(NULL),
35 num_buffers_(num_buffers),
36 buffer_(NULL),
37 channels_(channels) {
38 format_.wFormatTag = WAVE_FORMAT_PCM;
39 format_.nChannels = channels > 2 ? 2 : channels;
40 format_.nSamplesPerSec = sampling_rate;
41 format_.wBitsPerSample = 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_ = samples_per_packet * 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 if (state_ != kStateEmpty)
59 return false;
60 if (num_buffers_ < 2 || num_buffers_ > 10)
61 return false;
62 MMRESULT result = ::waveInOpen(&wavein_, device_id_, &format_,
63 reinterpret_cast<DWORD_PTR>(WaveCallback),
64 reinterpret_cast<DWORD_PTR>(this),
65 CALLBACK_FUNCTION);
66 if (result != MMSYSERR_NOERROR)
67 return false;
68
69 SetupBuffers();
70 state_ = kStateReady;
71 return true;
72 }
73
74 void PCMWaveInAudioInputStream::SetupBuffers() {
75 WAVEHDR* last = NULL;
76 WAVEHDR* first = NULL;
77 for (int ix = 0; ix != num_buffers_; ++ix) {
78 uint32 sz = sizeof(WAVEHDR) + buffer_size_;
79 buffer_ = reinterpret_cast<WAVEHDR*>(new char[sz]);
80 buffer_->lpData = reinterpret_cast<char*>(buffer_) + sizeof(WAVEHDR);
81 buffer_->dwBufferLength = buffer_size_;
82 buffer_->dwBytesRecorded = 0;
83 buffer_->dwUser = reinterpret_cast<DWORD_PTR>(last);
84 buffer_->dwFlags = WHDR_DONE;
85 buffer_->dwLoops = 0;
86 if (ix == 0)
87 first = buffer_;
88 last = buffer_;
89 ::waveInPrepareHeader(wavein_, buffer_, sizeof(WAVEHDR));
90 }
91 // Fix the first buffer to point to the last one.
92 first->dwUser = reinterpret_cast<DWORD_PTR>(last);
93 }
94
95 void PCMWaveInAudioInputStream::FreeBuffers() {
96 WAVEHDR* current = buffer_;
97 for (int ix = 0; ix != num_buffers_; ++ix) {
98 WAVEHDR* next = GetNextBuffer(current);
99 if (current->dwFlags & WHDR_PREPARED)
100 ::waveInUnprepareHeader(wavein_, current, sizeof(WAVEHDR));
101 delete[] reinterpret_cast<char*>(current);
102 current = next;
103 }
104 buffer_ = NULL;
105 }
106
107 void PCMWaveInAudioInputStream::Start(AudioInputCallback* callback) {
108 if (state_ != kStateReady)
109 return;
110
111 callback_ = callback;
112 state_ = kStateRecording;
113
114 WAVEHDR* buffer = buffer_;
115 for (int ix = 0; ix != num_buffers_; ++ix) {
116 QueueNextPacket(buffer);
117 buffer = GetNextBuffer(buffer);
118 }
119 buffer = buffer_;
120
121 MMRESULT result = ::waveInStart(wavein_);
122 if (result != MMSYSERR_NOERROR) {
123 HandleError(result);
124 state_ = kStateReady;
125 }
126 }
127
128 // Stopping is tricky. First, no buffer should be locked by the audio driver
129 // or else the waveInReset() will deadlock and secondly, the callback should
130 // not be inside the AudioInputCallback's OnData because waveInReset()
131 // forcefully kills the callback thread.
132 void PCMWaveInAudioInputStream::Stop() {
133 if (state_ != kStateRecording)
134 return;
135 state_ = kStateStopping;
136 // Wait for the callback to finish, it will signal us when ready to be reset.
137 if (WAIT_OBJECT_0 != ::WaitForSingleObject(stopped_event_, INFINITE)) {
138 HandleError(::GetLastError());
139 return;
140 }
141 state_ = kStateStopped;
142 MMRESULT res = ::waveInReset(wavein_);
143 if (res != MMSYSERR_NOERROR) {
144 state_ = kStateRecording;
145 HandleError(res);
146 return;
147 }
148 state_ = kStateReady;
149 }
150
151 // We can Close in any state except that when trying to close a stream that is
152 // recording Windows generates an error, which we propagate to the source.
153 void PCMWaveInAudioInputStream::Close() {
154 if (wavein_) {
155 // waveInClose generates a callback with WIM_CLOSE id in the same thread.
156 MMRESULT res = ::waveInClose(wavein_);
157 if (res != MMSYSERR_NOERROR) {
158 HandleError(res);
159 return;
160 }
161 state_ = kStateClosed;
162 wavein_ = NULL;
163 FreeBuffers();
164 }
165 // Tell the audio manager that we have been released. This can result in
166 // the manager destroying us in-place so this needs to be the last thing
167 // we do on this function.
168 manager_->ReleaseInputStream(this);
169 }
170
171 void PCMWaveInAudioInputStream::HandleError(MMRESULT error) {
172 DLOG(WARNING) << "PCMWaveInAudio error " << error;
173 callback_->OnError(this, error);
174 }
175
176 void PCMWaveInAudioInputStream::QueueNextPacket(WAVEHDR *buffer) {
177 MMRESULT res = ::waveInAddBuffer(wavein_, buffer, sizeof(WAVEHDR));
178 if (res != MMSYSERR_NOERROR)
179 HandleError(res);
180 }
181
182 // Windows calls us back in this function when some events happen. Most notably
183 // when it has an audio buffer with recorded data.
184 void PCMWaveInAudioInputStream::WaveCallback(HWAVEIN hwi, UINT msg,
185 DWORD_PTR instance,
186 DWORD_PTR param1, DWORD_PTR) {
187 PCMWaveInAudioInputStream* obj =
188 reinterpret_cast<PCMWaveInAudioInputStream*>(instance);
189
190 if (msg == WIM_DATA) {
191 // WIM_DONE indicates that the driver is done with our buffer. We pass it
192 // to the callback and check if we need to stop playing.
193 WAVEHDR* buffer = reinterpret_cast<WAVEHDR*>(param1);
194 obj->callback_->OnData(obj, reinterpret_cast<const uint8*>(buffer->lpData),
195 buffer->dwBytesRecorded);
196
197 if (obj->state_ == kStateStopping) {
198 // The main thread has called Stop() and is waiting to issue waveOutReset
199 // which will kill this thread. We should not enter AudioSourceCallback
200 // code anymore.
201 ::SetEvent(obj->stopped_event_);
202 } else if (obj->state_ == kStateStopped) {
203 // Not sure if ever hit this but just in case.
204 } else {
205 // Queue the finished buffer back with the audio driver. Since we are
206 // reusing the same buffers we can get away without calling
207 // waveInPrepareHeader.
208 obj->QueueNextPacket(buffer);
209 }
210 } else if (msg == WIM_CLOSE) {
211 // We can be closed before calling Start, so it is possible to have a
212 // null callback at this point.
213 if (obj->callback_)
214 obj->callback_->OnClose(obj);
215 }
216 }
OLDNEW
« no previous file with comments | « media/audio/win/wavein_input_win.h ('k') | media/media.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698