Panda3D

microphoneAudioDS.cxx
Go to the documentation of this file.
1 /**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file microphoneAudioDS.cxx
10 * @author jyelon
11 * @date 2007-11-01
12 *
13 * It goes against Panda3D coding style conventions to hide an
14 * entire class in a C++ file and not expose it through header
15 * files at all. However, in this case, these classes are so full
16 * of OS-specific junk that I feel it is better to hide them
17 * entirely. - Josh
18 */
19
20 #ifdef HAVE_DIRECTCAM
21
22 #ifndef WIN32_LEAN_AND_MEAN
23 #define WIN32_LEAN_AND_MEAN 1
24 #endif
25
26 #undef Configure
27
28 #include <windows.h>
29 #include <mmsystem.h>
30
31 /**
32 * The directshow implementation of microphones.
33 */
34 class MicrophoneAudioDS : public MicrophoneAudio {
35 public:
36 static void find_all_microphones_ds();
37 friend void find_all_microphones_ds();
38
39 private:
40 virtual PT(MovieAudioCursor) open();
41
42 int _device_id;
43 int _manufacturer_id;
44 int _product_id;
45
46 struct AudioBuf {
47 HGLOBAL _storage_gh;
48 HGLOBAL _header_gh;
49 LPSTR _storage;
50 LPWAVEHDR _header;
51 };
52 typedef pvector <AudioBuf> AudioBuffers;
53
54 static void delete_buffers(AudioBuffers &buffers);
55
56 friend class MicrophoneAudioCursorDS;
57
58 public:
59 static TypeHandle get_class_type() {
60 return _type_handle;
61 }
62 static void init_type() {
63 MicrophoneAudio::init_type();
64 register_type(_type_handle, "MicrophoneAudioDS",
65 MicrophoneAudio::get_class_type());
66 }
67 virtual TypeHandle get_type() const {
68 return get_class_type();
69 }
70 virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
71
72 private:
73 static TypeHandle _type_handle;
74 };
75
76 TypeHandle MicrophoneAudioDS::_type_handle;
77
78 /**
79 * The directshow implementation of microphones.
80 */
81
82 class MicrophoneAudioCursorDS : public MovieAudioCursor
83 {
84 public:
85 typedef MicrophoneAudioDS::AudioBuffers AudioBuffers;
86 MicrophoneAudioCursorDS(MicrophoneAudioDS *src, AudioBuffers &bufs, HWAVEIN hwav);
87 virtual ~MicrophoneAudioCursorDS();
88
89 AudioBuffers _buffers;
90 HWAVEIN _hwavein;
91 int _samples_per_buffer;
92
93 public:
94 virtual void read_samples(int n, int16_t *data);
95 virtual int ready() const;
96
97 public:
98 void cleanup();
99
100 HWAVEIN _handle;
101 int _next; // Which buffer is the next one to read from.
102 int _offset; // How many samples to skip in the buffer.
103
104 public:
105 static TypeHandle get_class_type() {
106 return _type_handle;
107 }
108 static void init_type() {
109 MovieAudioCursor::init_type();
110 register_type(_type_handle, "MicrophoneAudioCursorDS",
111 MovieAudioCursor::get_class_type());
112 }
113 virtual TypeHandle get_type() const {
114 return get_class_type();
115 }
116 virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
117
118 private:
119 static TypeHandle _type_handle;
120 };
121
122 TypeHandle MicrophoneAudioCursorDS::_type_handle;
123
124 /**
125 * Finds all DirectShow microphones and adds them to the global list
126 * _all_microphones.
127 */
128 void MicrophoneAudioDS::
129 find_all_microphones_ds() {
130 MMRESULT stat;
131 static int freqs[] = { 11025, 22050, 44100, 48000, 0 };
132
133 int ndevs = waveInGetNumDevs();
134 for (int i=0; i<ndevs; i++) {
135 WAVEINCAPS caps;
136 stat = waveInGetDevCaps(i, &caps, sizeof(caps));
137 if (stat != MMSYSERR_NOERROR) continue;
138 for (int chan=1; chan<=2; chan++) {
139 for (int fselect=0; freqs[fselect]; fselect++) {
140 WAVEFORMATEX format;
141 int freq = freqs[fselect];
142 format.wFormatTag = WAVE_FORMAT_PCM;
143 format.nChannels = chan;
144 format.nSamplesPerSec = freq;
145 format.nAvgBytesPerSec = freq * chan * 2;
146 format.nBlockAlign = 2 * chan;
147 format.wBitsPerSample = 16;
148 format.cbSize = 0;
149 stat = waveInOpen(nullptr, i, &format, 0, 0, WAVE_FORMAT_QUERY);
150 if (stat == MMSYSERR_NOERROR) {
151 PT(MicrophoneAudioDS) p = new MicrophoneAudioDS();
152 std::ostringstream name;
153 name << "WaveIn: " << caps.szPname << " Chan:" << chan << " HZ:" << freq;
154 p->set_name(name.str());
155 p->_device_id = i;
156 p->_manufacturer_id = caps.wMid;
157 p->_product_id = caps.wPid;
158 p->_rate = freq;
159 p->_channels = chan;
160 _all_microphones.push_back((MicrophoneAudioDS*)p);
161 }
162 }
163 }
164 }
165 }
166
167 void find_all_microphones_ds() {
168 MicrophoneAudioDS::init_type();
169 // MicrophoneAudioCursorDS::init_type();
170 MicrophoneAudioDS::find_all_microphones_ds();
171 }
172
173 /**
174 * Delete a set of audio buffers.
175 */
176 void MicrophoneAudioDS::
177 delete_buffers(AudioBuffers &buffers) {
178 for (int i=0; i<(int)buffers.size(); i++) {
179 AudioBuf &buf = buffers[i];
180 if (buf._header_gh) {
181 GlobalUnlock(buf._header_gh);
182 GlobalFree(buf._header_gh);
183 }
184 if (buf._storage_gh) {
185 GlobalUnlock(buf._storage_gh);
186 GlobalFree(buf._storage_gh);
187 }
188 }
189 buffers.clear();
190 }
191
192 /**
193 * Open this video, returning a MovieVideoCursor.
194 */
195 PT(MovieAudioCursor) MicrophoneAudioDS::
196 open() {
197
198 // Allocate the buffers. 64 buffers, not quite 120 sec each.
199 int samples;
200 switch (_rate) {
201 case 11025: samples=512; break;
202 case 22050: samples=1024; break;
203 case 44100: samples=2048; break;
204 }
205 int bytes = _channels * samples * 2;
206
207 bool failed = false;
208 AudioBuffers buffers;
209 for (int i=0; i<64; i++) {
210 AudioBuf buf;
211 buf._storage_gh = 0;
212 buf._header_gh = 0;
213 buf._storage = 0;
214 buf._header = 0;
215
216 buf._storage_gh = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, bytes);
217 buf._header_gh = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR));
218 if (buf._storage_gh != 0) {
219 buf._storage = (LPSTR)GlobalLock(buf._storage_gh);
220 }
221 if (buf._header_gh != 0) {
222 buf._header = (LPWAVEHDR)GlobalLock(buf._header_gh);
223 }
224 if (buf._storage && buf._header) {
225 ZeroMemory(buf._header, sizeof(WAVEHDR));
226 buf._header->lpData = buf._storage;
227 buf._header->dwBufferLength = bytes;
228 } else {
229 failed = true;
230 }
231 buffers.push_back(buf);
232 if (failed) break;
233 }
234
235 if (failed) {
236 delete_buffers(buffers);
237 nassert_raise("Could not allocate audio input buffers.");
238 return nullptr;
239 }
240
241 WAVEFORMATEX format;
242 format.wFormatTag = WAVE_FORMAT_PCM;
243 format.nChannels = _channels;
244 format.nSamplesPerSec = _rate;
245 format.nAvgBytesPerSec = _rate * _channels * 2;
246 format.nBlockAlign = 2 * _channels;
247 format.wBitsPerSample = 16;
248 format.cbSize = 0;
249
250 HWAVEIN hwav;
251 MMRESULT stat = waveInOpen(&hwav, _device_id, &format, 0, 0, CALLBACK_NULL);
252
253 if (stat != MMSYSERR_NOERROR) {
254 delete_buffers(buffers);
255 nassert_raise("Could not open audio input device.");
256 return nullptr;
257 }
258
259 for (int i=0; i<(int)buffers.size(); i++) {
260 stat = waveInPrepareHeader(hwav, buffers[i]._header, sizeof(WAVEHDR));
261 if (stat == MMSYSERR_NOERROR) {
262 stat = waveInAddBuffer(hwav, buffers[i]._header, sizeof(WAVEHDR));
263 }
264 if (stat != MMSYSERR_NOERROR) {
265 waveInClose(hwav);
266 delete_buffers(buffers);
267 nassert_raise("Could not queue buffers for audio input device.");
268 return nullptr;
269 }
270 }
271 stat = waveInStart(hwav);
272 if (stat != MMSYSERR_NOERROR) {
273 waveInClose(hwav);
274 delete_buffers(buffers);
275 nassert_raise("Could not start recording on input device.");
276 return nullptr;
277 }
278 return new MicrophoneAudioCursorDS(this, buffers, hwav);
279 }
280
281 /**
282 *
283 */
284 MicrophoneAudioCursorDS::
285 MicrophoneAudioCursorDS(MicrophoneAudioDS *src, AudioBuffers &bufs, HWAVEIN hwav) :
286 MovieAudioCursor(src),
287 _buffers(bufs),
288 _handle(hwav),
289 _next(0),
290 _offset(0)
291 {
292 _audio_rate = src->get_rate();
293 _audio_channels = src->get_channels();
294 _length = 1.0E10;
295 _can_seek = false;
296 _can_seek_fast = false;
297 _aborted = false;
298 _samples_per_buffer = bufs[0]._header->dwBufferLength / (2 * _audio_channels);
299 }
300
301 /**
302 *
303 */
304 void MicrophoneAudioCursorDS::
305 cleanup() {
306 if (_handle) {
307 waveInClose(_handle);
308 _handle = 0;
309 }
310 MicrophoneAudioDS::delete_buffers(_buffers);
311 _next = 0;
312 _offset = 0;
313 }
314
315 /**
316 *
317 */
318 MicrophoneAudioCursorDS::
319 ~MicrophoneAudioCursorDS() {
320 cleanup();
321 }
322
323 /**
324 *
325 */
326 void MicrophoneAudioCursorDS::
327 read_samples(int n, int16_t *data) {
328 int orign = n;
329 if (_handle) {
330 while (1) {
331 int index = _next % _buffers.size();
332 if ((_buffers[index]._header->dwFlags & WHDR_DONE)==0) {
333 break;
334 }
335
336 // Find start of data in buffer.
337 int16_t *src = (int16_t*)(_buffers[index]._storage);
338 src += (_offset * _audio_channels);
339
340 // Decide how many samples to extract from this buffer.
341 int samples = _samples_per_buffer;
342 samples -= _offset;
343 if (samples > n) samples = n;
344
345 // Copy data to output buffer.
346 memcpy(data, src, samples * 2 * _audio_channels);
347
348 // Advance pointers.
349 data += samples * _audio_channels;
350 n -= samples;
351 _offset += samples;
352 _samples_read += samples;
353 if (_offset != _samples_per_buffer) {
354 break;
355 }
356 _buffers[index]._header->dwFlags &= ~(WHDR_DONE);
357 MMRESULT stat = waveInUnprepareHeader(_handle, _buffers[index]._header, sizeof(WAVEHDR));
358 if (stat == MMSYSERR_NOERROR) {
359 stat = waveInPrepareHeader(_handle, _buffers[index]._header, sizeof(WAVEHDR));
360 }
361 if (stat == MMSYSERR_NOERROR) {
362 stat = waveInAddBuffer(_handle, _buffers[index]._header, sizeof(WAVEHDR));
363 }
364 if (stat != MMSYSERR_NOERROR) {
365 movies_cat.error() << "Could not requeue audio buffers, closing microphone.\n";
366 cleanup();
367 break;
368 }
369 _next += 1;
370 _offset = 0;
371 }
372 }
373 if (n > 0) {
374 memcpy(data, 0, n*2*_audio_channels);
375 }
376 }
377
378 /**
379 *
380 */
381 int MicrophoneAudioCursorDS::
382 ready() const {
383 if (_handle == 0) return 0;
384 int total = 0;
385 for (int i=0; i<(int)_buffers.size(); i++) {
386 int index = (_next + i) % (_buffers.size());
387 if ((_buffers[index]._header->dwFlags & WHDR_DONE)==0) {
388 break;
389 }
390 total += _samples_per_buffer;
391 }
392 total -= _offset;
393 return total;
394 }
395
396
397 #endif // HAVE_DIRECTSHOW
MicrophoneAudio
Class MicrophoneAudio provides the means to read raw audio samples from a microphone.
Definition: microphoneAudio.h:25
MovieAudioCursor
A MovieAudio is actually any source that provides a sequence of audio samples.
Definition: movieAudioCursor.h:34
MovieAudioCursor::ready
virtual int ready() const
Returns the number of audio samples that are ready to read.
Definition: movieAudioCursor.cxx:161
MovieAudioCursor::read_samples
void read_samples(int n, Datagram *dg)
Read audio samples from the stream into a Datagram.
Definition: movieAudioCursor.cxx:73
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
pvector
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
register_type
void register_type(TypeHandle &type_handle, const std::string &name)
This inline function is just a convenient way to call TypeRegistry::register_type(),...
Definition: register_type.I:22