Panda3D

microphoneAudioDS.cxx

00001 // Filename: microphoneAudioDS.cxx
00002 // Created by: jyelon (01Nov2007)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 //
00015 // It goes against Panda3D coding style conventions to hide an
00016 // entire class in a C++ file and not expose it through header
00017 // files at all.  However, in this case, these classes are so full
00018 // of OS-specific junk that I feel it is better to hide them
00019 // entirely.  - Josh
00020 //
00021 ////////////////////////////////////////////////////////////////////
00022 
00023 #ifdef HAVE_DIRECTCAM
00024 
00025 #define WIN32_LEAN_AND_MEAN 
00026 
00027 #undef Configure
00028 
00029 #include <windows.h>
00030 #include <mmsystem.h>
00031 
00032 ////////////////////////////////////////////////////////////////////
00033 //       Class : MicrophoneAudioDS
00034 // Description : The directshow implementation of microphones.
00035 ////////////////////////////////////////////////////////////////////
00036 
00037 class MicrophoneAudioDS : public MicrophoneAudio
00038 {
00039 public:
00040   static void find_all_microphones_ds();
00041   friend void find_all_microphones_ds();
00042 
00043 private:
00044   virtual PT(MovieAudioCursor) open();
00045 
00046   int _device_id;
00047   int _manufacturer_id;
00048   int _product_id;
00049 
00050   struct AudioBuf {
00051     HGLOBAL   _storage_gh;
00052     HGLOBAL   _header_gh;
00053     LPSTR     _storage;
00054     LPWAVEHDR _header;
00055   };
00056   typedef pvector <AudioBuf> AudioBuffers;
00057 
00058   static void delete_buffers(AudioBuffers &buffers);
00059   
00060   friend class MicrophoneAudioCursorDS;
00061 
00062 public:
00063   static TypeHandle get_class_type() {
00064     return _type_handle;
00065   }
00066   static void init_type() {
00067     MicrophoneAudio::init_type();
00068     register_type(_type_handle, "MicrophoneAudioDS",
00069                   MicrophoneAudio::get_class_type());
00070   }
00071   virtual TypeHandle get_type() const {
00072     return get_class_type();
00073   }
00074   virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
00075 
00076 private:
00077   static TypeHandle _type_handle;
00078 };
00079 
00080 TypeHandle MicrophoneAudioDS::_type_handle;
00081 
00082 ////////////////////////////////////////////////////////////////////
00083 //       Class : MicrophoneAudioCursorDS
00084 // Description : The directshow implementation of microphones.
00085 ////////////////////////////////////////////////////////////////////
00086 
00087 class MicrophoneAudioCursorDS : public MovieAudioCursor
00088 {
00089 public:
00090   typedef MicrophoneAudioDS::AudioBuffers AudioBuffers;
00091   MicrophoneAudioCursorDS(MicrophoneAudioDS *src, AudioBuffers &bufs, HWAVEIN hwav);
00092   virtual ~MicrophoneAudioCursorDS();
00093   
00094   AudioBuffers _buffers;
00095   HWAVEIN _hwavein;
00096   int _samples_per_buffer;
00097 
00098 public:
00099   virtual void read_samples(int n, PN_int16 *data);
00100   virtual int ready() const;
00101 
00102 public:
00103   void cleanup();
00104 
00105   HWAVEIN _handle;
00106   int     _next;    // Which buffer is the next one to read from.
00107   int     _offset;  // How many samples to skip in the buffer.
00108   
00109 public:
00110   static TypeHandle get_class_type() {
00111     return _type_handle;
00112   }
00113   static void init_type() {
00114     MovieAudioCursor::init_type();
00115     register_type(_type_handle, "MicrophoneAudioCursorDS",
00116                   MovieAudioCursor::get_class_type());
00117   }
00118   virtual TypeHandle get_type() const {
00119     return get_class_type();
00120   }
00121   virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
00122 
00123 private:
00124   static TypeHandle _type_handle;
00125 };
00126 
00127 TypeHandle MicrophoneAudioCursorDS::_type_handle;
00128 
00129 ////////////////////////////////////////////////////////////////////
00130 //     Function: MicrophoneAudioDS::find_all_microphones_ds
00131 //       Access: Public, Static
00132 //  Description: Finds all DirectShow microphones and adds them to 
00133 //               the global list _all_microphones.
00134 ////////////////////////////////////////////////////////////////////
00135 void MicrophoneAudioDS::
00136 find_all_microphones_ds() {
00137   MMRESULT stat;
00138   static int freqs[] = { 11025, 22050, 44100, 48000, 0 };
00139 
00140   int ndevs = waveInGetNumDevs();
00141   for (int i=0; i<ndevs; i++) {
00142     WAVEINCAPS caps;
00143     stat = waveInGetDevCaps(i, &caps, sizeof(caps));
00144     if (stat != MMSYSERR_NOERROR) continue;
00145     for (int chan=1; chan<=2; chan++) {
00146       for (int fselect=0; freqs[fselect]; fselect++) {
00147         WAVEFORMATEX format;
00148         int freq = freqs[fselect];
00149         format.wFormatTag = WAVE_FORMAT_PCM;
00150         format.nChannels = chan;
00151         format.nSamplesPerSec = freq;
00152         format.nAvgBytesPerSec = freq * chan * 2;
00153         format.nBlockAlign = 2 * chan;
00154         format.wBitsPerSample = 16;
00155         format.cbSize = 0;
00156         stat = waveInOpen(NULL, i, &format, NULL, NULL, WAVE_FORMAT_QUERY);
00157         if (stat == MMSYSERR_NOERROR) {
00158           PT(MicrophoneAudioDS) p = new MicrophoneAudioDS();
00159           ostringstream name;
00160           name << "WaveIn: " << caps.szPname << " Chan:" << chan << " HZ:" << freq;
00161           p->set_name(name.str());
00162           p->_device_id = i;
00163           p->_manufacturer_id = caps.wMid;
00164           p->_product_id = caps.wPid;
00165           p->_rate = freq;
00166           p->_channels = chan;
00167           _all_microphones.push_back((MicrophoneAudioDS*)p);
00168         }
00169       }
00170     }
00171   }
00172 }
00173 
00174 void find_all_microphones_ds() {
00175   MicrophoneAudioDS::init_type();
00176   //  MicrophoneAudioCursorDS::init_type();
00177   MicrophoneAudioDS::find_all_microphones_ds();
00178 }
00179 
00180 ////////////////////////////////////////////////////////////////////
00181 //     Function: MicrophoneAudioDS::delete_buffers
00182 //       Access: Private, Static
00183 //  Description: Delete a set of audio buffers.
00184 ////////////////////////////////////////////////////////////////////
00185 void MicrophoneAudioDS::
00186 delete_buffers(AudioBuffers &buffers) {
00187   for (int i=0; i<(int)buffers.size(); i++) {
00188     AudioBuf &buf = buffers[i];
00189     if (buf._header_gh) {
00190       GlobalUnlock(buf._header_gh);
00191       GlobalFree(buf._header_gh);
00192     }
00193     if (buf._storage_gh) {
00194       GlobalUnlock(buf._storage_gh);
00195       GlobalFree(buf._storage_gh);
00196     }
00197   }
00198   buffers.clear();
00199 }
00200 
00201 ////////////////////////////////////////////////////////////////////
00202 //     Function: MicrophoneAudioDS::open
00203 //       Access: Published, Virtual
00204 //  Description: Open this video, returning a MovieVideoCursor.
00205 ////////////////////////////////////////////////////////////////////
00206 PT(MovieAudioCursor) MicrophoneAudioDS::
00207 open() {
00208   
00209   // Allocate the buffers. 64 buffers, not quite 1/20 sec each.
00210   int samples;
00211   switch (_rate) {
00212   case 11025: samples=512; break;
00213   case 22050: samples=1024; break;
00214   case 44100: samples=2048; break;
00215   }
00216   int bytes = _channels * samples * 2;
00217 
00218   bool failed = false;
00219   AudioBuffers buffers;
00220   for (int i=0; i<64; i++) {
00221     AudioBuf buf;
00222     buf._storage_gh = 0;
00223     buf._header_gh = 0;
00224     buf._storage = 0;
00225     buf._header = 0;
00226 
00227     buf._storage_gh = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, bytes);
00228     buf._header_gh = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR));
00229     if (buf._storage_gh != 0) {
00230       buf._storage = (LPSTR)GlobalLock(buf._storage_gh);
00231     }
00232     if (buf._header_gh != 0) {
00233       buf._header = (LPWAVEHDR)GlobalLock(buf._header_gh);
00234     }
00235     if (buf._storage && buf._header) {
00236       ZeroMemory(buf._header, sizeof(WAVEHDR));
00237       buf._header->lpData = buf._storage;
00238       buf._header->dwBufferLength = bytes;
00239     } else {
00240       failed = true;
00241     }
00242     buffers.push_back(buf);
00243     if (failed) break;
00244   }
00245 
00246   if (failed) {
00247     delete_buffers(buffers);
00248     nassert_raise("Could not allocate audio input buffers.");
00249     return NULL;
00250   }
00251   
00252   WAVEFORMATEX format;
00253   format.wFormatTag = WAVE_FORMAT_PCM;
00254   format.nChannels = _channels;
00255   format.nSamplesPerSec = _rate;
00256   format.nAvgBytesPerSec = _rate * _channels * 2;
00257   format.nBlockAlign = 2 * _channels;
00258   format.wBitsPerSample = 16;
00259   format.cbSize = 0;
00260 
00261   HWAVEIN hwav;
00262   MMRESULT stat = waveInOpen(&hwav, _device_id, &format, NULL, NULL, CALLBACK_NULL);
00263   
00264   if (stat != MMSYSERR_NOERROR) {
00265     delete_buffers(buffers);
00266     nassert_raise("Could not open audio input device.");
00267     return NULL;
00268   }
00269 
00270   for (int i=0; i<(int)buffers.size(); i++) {
00271     stat = waveInPrepareHeader(hwav, buffers[i]._header, sizeof(WAVEHDR));
00272     if (stat == MMSYSERR_NOERROR) {
00273       stat = waveInAddBuffer(hwav, buffers[i]._header, sizeof(WAVEHDR));
00274     }
00275     if (stat != MMSYSERR_NOERROR) {
00276       waveInClose(hwav);
00277       delete_buffers(buffers);
00278       nassert_raise("Could not queue buffers for audio input device.");
00279       return NULL;
00280     }
00281   }
00282   stat = waveInStart(hwav);
00283   if (stat != MMSYSERR_NOERROR) {
00284     waveInClose(hwav);
00285     delete_buffers(buffers);
00286     nassert_raise("Could not start recording on input device.");
00287     return NULL;    
00288   }
00289   return new MicrophoneAudioCursorDS(this, buffers, hwav);
00290 }
00291 
00292 ////////////////////////////////////////////////////////////////////
00293 //     Function: MicrophoneAudioCursorDS::Constructor
00294 //       Access: Published
00295 //  Description: 
00296 ////////////////////////////////////////////////////////////////////
00297 MicrophoneAudioCursorDS::
00298 MicrophoneAudioCursorDS(MicrophoneAudioDS *src, AudioBuffers &bufs, HWAVEIN hwav) :
00299   MovieAudioCursor(src),
00300   _buffers(bufs),
00301   _handle(hwav),
00302   _next(0),
00303   _offset(0)
00304 {
00305   _audio_rate = src->get_rate();
00306   _audio_channels = src->get_channels();
00307   _length = 1.0E10;
00308   _can_seek = false;
00309   _can_seek_fast = false;
00310   _aborted = false;
00311   _samples_per_buffer = bufs[0]._header->dwBufferLength / (2 * _audio_channels);
00312 }
00313 
00314 ////////////////////////////////////////////////////////////////////
00315 //     Function: MicrophoneAudioCursorDS::cleanup
00316 //       Access: Published
00317 //  Description: 
00318 ////////////////////////////////////////////////////////////////////
00319 void MicrophoneAudioCursorDS::
00320 cleanup() {
00321   if (_handle) {
00322     waveInClose(_handle);
00323     _handle = 0;
00324   }
00325   MicrophoneAudioDS::delete_buffers(_buffers);
00326   _next = 0;
00327   _offset = 0;
00328 }
00329 
00330 ////////////////////////////////////////////////////////////////////
00331 //     Function: MicrophoneAudioCursorDS::Destructor
00332 //       Access: Published
00333 //  Description: 
00334 ////////////////////////////////////////////////////////////////////
00335 MicrophoneAudioCursorDS::
00336 ~MicrophoneAudioCursorDS() {
00337   cleanup();
00338 }
00339 
00340 ////////////////////////////////////////////////////////////////////
00341 //     Function: MicrophoneAudioCursorDS::read_samples
00342 //       Access: Published
00343 //  Description: 
00344 ////////////////////////////////////////////////////////////////////
00345 void MicrophoneAudioCursorDS::
00346 read_samples(int n, PN_int16 *data) {
00347   int orign = n;
00348   if (_handle) {
00349     while (1) {
00350       int index = _next % _buffers.size();
00351       if ((_buffers[index]._header->dwFlags & WHDR_DONE)==0) {
00352         break;
00353       }
00354       
00355       // Find start of data in buffer.
00356       PN_int16 *src = (PN_int16*)(_buffers[index]._storage);
00357       src += (_offset * _audio_channels);
00358       
00359       // Decide how many samples to extract from this buffer.
00360       int samples = _samples_per_buffer;
00361       samples -= _offset;
00362       if (samples > n) samples = n;
00363       
00364       // Copy data to output buffer.
00365       memcpy(data, src, samples * 2 * _audio_channels);
00366       
00367       // Advance pointers.
00368       data += samples * _audio_channels;
00369       n -= samples;
00370       _offset += samples;
00371       _samples_read += samples;
00372       if (_offset != _samples_per_buffer) {
00373         break;
00374       }
00375       _buffers[index]._header->dwFlags &= ~(WHDR_DONE);
00376       MMRESULT stat = waveInUnprepareHeader(_handle, _buffers[index]._header, sizeof(WAVEHDR));
00377       if (stat == MMSYSERR_NOERROR) {
00378         stat = waveInPrepareHeader(_handle, _buffers[index]._header, sizeof(WAVEHDR));
00379       }
00380       if (stat == MMSYSERR_NOERROR) {
00381         stat = waveInAddBuffer(_handle, _buffers[index]._header, sizeof(WAVEHDR));
00382       }
00383       if (stat != MMSYSERR_NOERROR) {
00384         movies_cat.error() << "Could not requeue audio buffers, closing microphone.\n";
00385         cleanup();
00386         break;
00387       }
00388       _next += 1;
00389       _offset = 0;
00390     }
00391   }
00392   if (n > 0) {
00393     memcpy(data, 0, n*2*_audio_channels);
00394   }
00395 }
00396 
00397 ////////////////////////////////////////////////////////////////////
00398 //     Function: MicrophoneAudioCursorDS::ready
00399 //       Access: Published
00400 //  Description: 
00401 ////////////////////////////////////////////////////////////////////
00402 int MicrophoneAudioCursorDS::
00403 ready() const {
00404   if (_handle == 0) return 0;
00405   int total = 0;
00406   for (int i=0; i<(int)_buffers.size(); i++) {
00407     int index = (_next + i) % (_buffers.size());
00408     if ((_buffers[index]._header->dwFlags & WHDR_DONE)==0) {
00409       break;
00410     }
00411     total += _samples_per_buffer;
00412   }
00413   total -= _offset;
00414   return total;
00415 }
00416 
00417 
00418 #endif // HAVE_DIRECTSHOW
 All Classes Functions Variables Enumerations