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