diff --git a/panda/src/movies/Sources.pp b/panda/src/movies/Sources.pp index 54e8a07f11..805c5a18a6 100644 --- a/panda/src/movies/Sources.pp +++ b/panda/src/movies/Sources.pp @@ -26,6 +26,7 @@ userDataAudio.h userDataAudio.I \ userDataAudioCursor.h userDataAudioCursor.I \ webcamVideo.h webcamVideo.I \ + microphoneAudio.h microphoneAudio.I \ config_movies.h #define INCLUDED_SOURCES \ @@ -44,6 +45,8 @@ userDataAudioCursor.cxx \ webcamVideo.cxx \ webcamVideoDS.cxx \ + microphoneAudio.cxx \ + microphoneAudioDS.cxx \ config_movies.cxx #define INSTALL_HEADERS \ @@ -59,6 +62,7 @@ ffmpegAudioCursor.h ffmpegAudioCursor.I \ ffmpegVirtualFile.h ffmpegVirtualFile.I \ webcamVideo.h webcamVideo.I \ + microphoneAudio.h microphoneAudio.I \ config_movies.h #define IGATESCAN all diff --git a/panda/src/movies/config_movies.cxx b/panda/src/movies/config_movies.cxx index efd362f7cf..3f84708e23 100644 --- a/panda/src/movies/config_movies.cxx +++ b/panda/src/movies/config_movies.cxx @@ -56,6 +56,7 @@ init_libmovies() { UserDataAudio::init_type(); UserDataAudioCursor::init_type(); WebcamVideo::init_type(); + MicrophoneAudio::init_type(); #ifdef HAVE_FFMPEG FfmpegVideo::init_type(); FfmpegVideoCursor::init_type(); diff --git a/panda/src/movies/microphoneAudio.I b/panda/src/movies/microphoneAudio.I new file mode 100644 index 0000000000..d4ec6f713b --- /dev/null +++ b/panda/src/movies/microphoneAudio.I @@ -0,0 +1,38 @@ +// Filename: microphoneAudio.I +// Created by: jyelon (02Jul07) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////// +// Function: MicrophoneAudio::get_channels +// Access: Published +// Description: Returns the number of channels. +//////////////////////////////////////////////////////////////////// +INLINE int MicrophoneAudio:: +get_channels() const { + return _channels; +} + +//////////////////////////////////////////////////////////////////// +// Function: MicrophoneAudio::get_rate +// Access: Published +// Description: Returns the sample rate. +//////////////////////////////////////////////////////////////////// +INLINE int MicrophoneAudio:: +get_rate() const { + return _rate; +} + diff --git a/panda/src/movies/microphoneAudio.cxx b/panda/src/movies/microphoneAudio.cxx new file mode 100644 index 0000000000..71ceff0cfb --- /dev/null +++ b/panda/src/movies/microphoneAudio.cxx @@ -0,0 +1,90 @@ +// Filename: microphoneAudio.cxx +// Created by: jyelon (02Jul07) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2007, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net +// +//////////////////////////////////////////////////////////////////// + +#include "microphoneAudio.h" +#include "movieAudioCursor.h" + +pvector MicrophoneAudio::_all_microphones; +TypeHandle MicrophoneAudio::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: MicrophoneAudio::Destructor +// Access: Published, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +MicrophoneAudio:: +~MicrophoneAudio() { +} + +//////////////////////////////////////////////////////////////////// +// Function: MicrophoneAudio::find_all_microphones +// Access: Public +// Description: Scans the hardware for microphones, and pushes them +// onto the global list of all microphones. +// +// There are several implementations of MicrophoneAudio, +// including one based on DirectShow, one based on +// Linux ALSA, and so forth. These implementations +// are contained in one C++ file each, and they export +// nothing at all except a single "find_all" function. +// Otherwise, they can only be accessed through the +// virtual methods of the MicrophoneAudio objects they +// create. +//////////////////////////////////////////////////////////////////// +void MicrophoneAudio:: +find_all_microphones() { + static bool initialized = false; + if (initialized) return; + initialized = true; + +#ifdef HAVE_DIRECTCAM + extern void find_all_microphones_ds(); + find_all_microphones_ds(); +#endif + +#ifdef HAVE_ALSA + extern void find_all_microphones_alsa(); + find_all_microphones_alsa(); +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: MicrophoneAudio::get_num_options +// Access: Public +// Description: Returns the number of microphone options. An "option" +// consists of a device plus a set of configuration +// parameters. For example, "Soundblaster Audigy Line in +// at 44,100 samples/sec" would be an option. +//////////////////////////////////////////////////////////////////// +int MicrophoneAudio:: +get_num_options() { + find_all_microphones(); + return _all_microphones.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MicrophoneAudio::get_option +// Access: Public +// Description: Returns the nth microphone option. +//////////////////////////////////////////////////////////////////// +PT(MicrophoneAudio) MicrophoneAudio:: +get_option(int n) { + find_all_microphones(); + nassertr((n >= 0) && (n < (int)_all_microphones.size()), NULL); + return _all_microphones[n]; +} diff --git a/panda/src/movies/microphoneAudio.h b/panda/src/movies/microphoneAudio.h new file mode 100644 index 0000000000..a3219f815e --- /dev/null +++ b/panda/src/movies/microphoneAudio.h @@ -0,0 +1,73 @@ +// Filename: microphoneAudio.h +// Created by: jyelon (02Jul07) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef MICROPHONEAUDIO_H +#define MICROPHONEAUDIO_H + +#include "movieAudio.h" +class MovieAudio; +class MovieAudioCursor; + +//////////////////////////////////////////////////////////////////// +// Class : MicrophoneAudio +// Description : Class MicrophoneAudio provides the means to read +// raw audio samples from a microphone. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA_MOVIES MicrophoneAudio : public MovieAudio { + + PUBLISHED: + virtual ~MicrophoneAudio(); + + static int get_num_options(); + static PT(MicrophoneAudio) get_option(int n); + + INLINE int get_channels() const; + INLINE int get_rate() const; + + virtual PT(MovieAudioCursor) open() = 0; + +public: + static void find_all_microphones(); + +protected: + int _channels; + int _rate; + + static pvector _all_microphones; + + public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedWritableReferenceCount::init_type(); + register_type(_type_handle, "MicrophoneAudio", + MovieAudio::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + + private: + static TypeHandle _type_handle; +}; + +#include "microphoneAudio.I" + +#endif diff --git a/panda/src/movies/microphoneAudioDS.cxx b/panda/src/movies/microphoneAudioDS.cxx new file mode 100644 index 0000000000..09622902e7 --- /dev/null +++ b/panda/src/movies/microphoneAudioDS.cxx @@ -0,0 +1,422 @@ +// Filename: microphoneAudioDS.cxx +// Created by: jyelon (01Nov2007) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2007, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// +// +// It goes against Panda3D coding style conventions to hide an +// entire class in a C++ file and not expose it through header +// files at all. However, in this case, these classes are so full +// of OS-specific junk that I feel it is better to hide them +// entirely. - Josh +// +//////////////////////////////////////////////////////////////////// + +#ifdef HAVE_DIRECTCAM + +#define WIN32_LEAN_AND_MEAN + +#undef Configure + +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : MicrophoneAudioDS +// Description : The directshow implementation of microphones. +//////////////////////////////////////////////////////////////////// + +class MicrophoneAudioDS : public MicrophoneAudio +{ +public: + static void find_all_microphones_ds(); + friend void find_all_microphones_ds(); + +private: + virtual PT(MovieAudioCursor) open(); + + int _device_id; + int _manufacturer_id; + int _product_id; + + struct AudioBuf { + HGLOBAL _storage_gh; + HGLOBAL _header_gh; + LPSTR _storage; + LPWAVEHDR _header; + }; + typedef pvector AudioBuffers; + + static void delete_buffers(AudioBuffers &buffers); + + friend class MicrophoneAudioCursorDS; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + MicrophoneAudio::init_type(); + register_type(_type_handle, "MicrophoneAudioDS", + MicrophoneAudio::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +TypeHandle MicrophoneAudioDS::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Class : MicrophoneAudioCursorDS +// Description : The directshow implementation of microphones. +//////////////////////////////////////////////////////////////////// + +class MicrophoneAudioCursorDS : public MovieAudioCursor +{ +public: + typedef MicrophoneAudioDS::AudioBuffers AudioBuffers; + MicrophoneAudioCursorDS(MicrophoneAudioDS *src, AudioBuffers &bufs, HWAVEIN hwav); + virtual ~MicrophoneAudioCursorDS(); + + AudioBuffers _buffers; + HWAVEIN _hwavein; + int _samples_per_buffer; + +public: + virtual void read_samples(int n, PN_int16 *data); + virtual int ready() const; + +public: + void cleanup(); + + HWAVEIN _handle; + int _next; // Which buffer is the next one to read from. + int _offset; // How many samples to skip in the buffer. + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + MovieAudioCursor::init_type(); + register_type(_type_handle, "MicrophoneAudioCursorDS", + MovieAudioCursor::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +TypeHandle MicrophoneAudioCursorDS::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: MicrophoneAudioDS::find_all_microphones_ds +// Access: Public, Static +// Description: Finds all DirectShow microphones and adds them to +// the global list _all_microphones. +//////////////////////////////////////////////////////////////////// +void MicrophoneAudioDS:: +find_all_microphones_ds() { + MMRESULT stat; + static int freqs[] = { 11025, 22050, 44100, 48000, 0 }; + + int ndevs = waveInGetNumDevs(); + for (int i=0; iset_name(name.str()); + p->_device_id = i; + p->_manufacturer_id = caps.wMid; + p->_product_id = caps.wPid; + p->_rate = freq; + p->_channels = chan; + _all_microphones.push_back((MicrophoneAudioDS*)p); + } + } + } + } +} + +void find_all_microphones_ds() { + MicrophoneAudioDS::init_type(); + // MicrophoneAudioCursorDS::init_type(); + MicrophoneAudioDS::find_all_microphones_ds(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MicrophoneAudioDS::delete_buffers +// Access: Private, Static +// Description: Delete a set of audio buffers. +//////////////////////////////////////////////////////////////////// +void MicrophoneAudioDS:: +delete_buffers(AudioBuffers &buffers) { + for (int i=0; i<(int)buffers.size(); i++) { + AudioBuf &buf = buffers[i]; + if (buf._header_gh) { + GlobalUnlock(buf._header_gh); + GlobalFree(buf._header_gh); + } + if (buf._storage_gh) { + GlobalUnlock(buf._storage_gh); + GlobalFree(buf._storage_gh); + } + } + buffers.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MicrophoneAudioDS::open +// Access: Published, Virtual +// Description: Open this video, returning a MovieVideoCursor. +//////////////////////////////////////////////////////////////////// +PT(MovieAudioCursor) MicrophoneAudioDS:: +open() { + + // Allocate the buffers. 64 buffers, not quite 1/20 sec each. + int samples; + switch (_rate) { + case 11025: samples=512; break; + case 22050: samples=1024; break; + case 44100: samples=2048; break; + } + int bytes = _channels * samples * 2; + + bool failed = false; + AudioBuffers buffers; + for (int i=0; i<64; i++) { + AudioBuf buf; + buf._storage_gh = 0; + buf._header_gh = 0; + buf._storage = 0; + buf._header = 0; + + buf._storage_gh = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, bytes); + buf._header_gh = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR)); + if (buf._storage_gh != 0) { + buf._storage = (LPSTR)GlobalLock(buf._storage_gh); + } + if (buf._header_gh != 0) { + buf._header = (LPWAVEHDR)GlobalLock(buf._header_gh); + } + if (buf._storage && buf._header) { + ZeroMemory(buf._header, sizeof(WAVEHDR)); + buf._header->lpData = buf._storage; + buf._header->dwBufferLength = bytes; + } else { + failed = true; + } + buffers.push_back(buf); + if (failed) break; + } + + if (failed) { + delete_buffers(buffers); + nassert_raise("Could not allocate audio input buffers."); + return NULL; + } + + WAVEFORMATEX format; + format.wFormatTag = WAVE_FORMAT_PCM; + format.nChannels = _channels; + format.nSamplesPerSec = _rate; + format.nAvgBytesPerSec = _rate * _channels * 2; + format.nBlockAlign = 2 * _channels; + format.wBitsPerSample = 16; + format.cbSize = 0; + + HWAVEIN hwav; + MMRESULT stat = waveInOpen(&hwav, _device_id, &format, NULL, NULL, CALLBACK_NULL); + + if (stat != MMSYSERR_NOERROR) { + delete_buffers(buffers); + nassert_raise("Could not open audio input device."); + return NULL; + } + + for (int i=0; i<(int)buffers.size(); i++) { + stat = waveInPrepareHeader(hwav, buffers[i]._header, sizeof(WAVEHDR)); + if (stat == MMSYSERR_NOERROR) { + stat = waveInAddBuffer(hwav, buffers[i]._header, sizeof(WAVEHDR)); + } + if (stat != MMSYSERR_NOERROR) { + waveInClose(hwav); + delete_buffers(buffers); + nassert_raise("Could not queue buffers for audio input device."); + return NULL; + } + } + stat = waveInStart(hwav); + if (stat != MMSYSERR_NOERROR) { + waveInClose(hwav); + delete_buffers(buffers); + nassert_raise("Could not start recording on input device."); + return NULL; + } + return new MicrophoneAudioCursorDS(this, buffers, hwav); +} + +//////////////////////////////////////////////////////////////////// +// Function: MicrophoneAudioCursorDS::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +MicrophoneAudioCursorDS:: +MicrophoneAudioCursorDS(MicrophoneAudioDS *src, AudioBuffers &bufs, HWAVEIN hwav) : + MovieAudioCursor(src), + _buffers(bufs), + _handle(hwav), + _next(0), + _offset(0) +{ + _audio_rate = src->get_rate(); + _audio_channels = src->get_channels(); + _length = 1.0E10; + _can_seek = false; + _can_seek_fast = false; + _aborted = false; + _samples_per_buffer = bufs[0]._header->dwBufferLength / (2 * _audio_channels); +} + +//////////////////////////////////////////////////////////////////// +// Function: MicrophoneAudioCursorDS::cleanup +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void MicrophoneAudioCursorDS:: +cleanup() { + if (_handle) { + waveInClose(_handle); + _handle = 0; + } + MicrophoneAudioDS::delete_buffers(_buffers); + _next = 0; + _offset = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: MicrophoneAudioCursorDS::Destructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +MicrophoneAudioCursorDS:: +~MicrophoneAudioCursorDS() { + cleanup(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MicrophoneAudioCursorDS::fetch_into_buffer +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void MicrophoneAudioCursorDS:: +read_samples(int n, PN_int16 *data) { + int orign = n; + if (_handle) { + while (1) { + int index = _next % _buffers.size(); + if ((_buffers[index]._header->dwFlags & WHDR_DONE)==0) { + break; + } + + // Find start of data in buffer. + PN_int16 *src = (PN_int16*)(_buffers[index]._storage); + src += (_offset * _audio_channels); + + // Decide how many samples to extract from this buffer. + int samples = _samples_per_buffer; + samples -= _offset; + if (samples > n) samples = n; + + // Copy data to output buffer. + memcpy(data, src, samples * 2 * _audio_channels); + + // Advance pointers. + data += samples * _audio_channels; + n -= samples; + _offset += samples; + _samples_read += samples; + if (_offset != _samples_per_buffer) { + break; + } + _buffers[index]._header->dwFlags &= ~(WHDR_DONE); + MMRESULT stat = waveInUnprepareHeader(_handle, _buffers[index]._header, sizeof(WAVEHDR)); + if (stat == MMSYSERR_NOERROR) { + stat = waveInPrepareHeader(_handle, _buffers[index]._header, sizeof(WAVEHDR)); + } + if (stat == MMSYSERR_NOERROR) { + stat = waveInAddBuffer(_handle, _buffers[index]._header, sizeof(WAVEHDR)); + } + if (stat != MMSYSERR_NOERROR) { + movies_cat.error() << "Could not requeue audio buffers, closing microphone.\n"; + cleanup(); + break; + } + _next += 1; + _offset = 0; + } + } + if (n > 0) { + memcpy(data, 0, n*2*_audio_channels); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MicrophoneAudioCursorDS::ready +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +int MicrophoneAudioCursorDS:: +ready() const { + if (_handle == 0) return 0; + int total = 0; + for (int i=0; i<(int)_buffers.size(); i++) { + int index = (_next + i) % (_buffers.size()); + if ((_buffers[index]._header->dwFlags & WHDR_DONE)==0) { + break; + } + total += _samples_per_buffer; + } + total -= _offset; + return total; +} + + +#endif // HAVE_DIRECTSHOW diff --git a/panda/src/movies/movieAudioCursor.I b/panda/src/movies/movieAudioCursor.I index 196e0837d1..c89d544363 100644 --- a/panda/src/movies/movieAudioCursor.I +++ b/panda/src/movies/movieAudioCursor.I @@ -74,40 +74,6 @@ length() const { return _length; } -//////////////////////////////////////////////////////////////////// -// Function: MovieAudioCursor::ready -// Access: Public -// Description: Returns the number of audio samples that are ready -// to read. This is primarily relevant for sources like -// microphones which produce samples at a fixed rate. -// If you try to read more samples than are ready, the -// result will be silent samples. -// -// Some audio streams do not have a limit on how fast -// they can produce samples. Such streams will always -// return 0x40000000 as the ready-count. This may well -// exceed the length of the audio stream. You therefore -// need to check length separately. -// -// If the aborted flag is set, that means the ready count -// is no longer being replenished. For example, a -// MovieAudioCursor might be reading from an internet -// radio station, and it might buffer data to avoid -// underruns. If it loses connection to the radio -// station, it will set the aborted flag to indicate that -// the buffer is no longer being replenished. But it is -// still ok to read the samples that are in the buffer, -// at least until they run out. Once those are gone, -// there will be no more. -// -// An audio consumer needs to check the length, the -// ready status, and the aborted flag. -//////////////////////////////////////////////////////////////////// -INLINE int MovieAudioCursor:: -ready() const { - return _ready; -} - //////////////////////////////////////////////////////////////////// // Function: MovieAudioCursor::can_seek // Access: Public @@ -165,6 +131,6 @@ tell() const { //////////////////////////////////////////////////////////////////// INLINE void MovieAudioCursor:: skip_samples(int n) { - read_samples(n, 0); + read_samples(n, (PN_int16*)0); } diff --git a/panda/src/movies/movieAudioCursor.cxx b/panda/src/movies/movieAudioCursor.cxx index f35d6a582c..327691c9c9 100644 --- a/panda/src/movies/movieAudioCursor.cxx +++ b/panda/src/movies/movieAudioCursor.cxx @@ -36,7 +36,6 @@ MovieAudioCursor(MovieAudio *src) : _length(1.0E10), _can_seek(true), _can_seek_fast(true), - _ready(0x40000000), _aborted(false), _samples_read(0) { @@ -69,12 +68,69 @@ read_samples(int n, PN_int16 *data) { return; } - for (int i=0; i 0) { + int blocksize = (4096 / _audio_channels); + if (blocksize > n) blocksize = n; + int words = blocksize * _audio_channels; + read_samples(blocksize, tmp); + for (int i=0; iadd_int16(tmp[i]); + } + n -= blocksize; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MovieAudioCursor::read_samples +// Access: Published +// Description: Read audio samples from the stream and returns +// them as a string. The samples are stored little-endian +// in the string. N is the number of samples you wish +// to read. Multiple-channel audio will be interleaved. +// +// This is not particularly efficient, but it may be +// a convenient way to manipulate samples in python. +//////////////////////////////////////////////////////////////////// +string MovieAudioCursor:: +read_samples(int n) { + ostringstream result; + PN_int16 tmp[4096]; + while (n > 0) { + int blocksize = (4096 / _audio_channels); + if (blocksize > n) blocksize = n; + int words = blocksize * _audio_channels; + read_samples(blocksize, tmp); + for (int i=0; i>8) & 255)); + } + n -= blocksize; + } + return result.str(); +} + + //////////////////////////////////////////////////////////////////// // Function: MovieAudioCursor::seek // Access: Published, Virtual @@ -103,3 +159,37 @@ seek(double offset) { _samples_read = 0; } +//////////////////////////////////////////////////////////////////// +// Function: MovieAudioCursor::ready +// Access: Public +// Description: Returns the number of audio samples that are ready +// to read. This is primarily relevant for sources like +// microphones which produce samples at a fixed rate. +// If you try to read more samples than are ready, the +// result will be silent samples. +// +// Some audio streams do not have a limit on how fast +// they can produce samples. Such streams will always +// return 0x40000000 as the ready-count. This may well +// exceed the length of the audio stream. You therefore +// need to check length separately. +// +// If the aborted flag is set, that means the ready count +// is no longer being replenished. For example, a +// MovieAudioCursor might be reading from an internet +// radio station, and it might buffer data to avoid +// underruns. If it loses connection to the radio +// station, it will set the aborted flag to indicate that +// the buffer is no longer being replenished. But it is +// still ok to read the samples that are in the buffer, +// at least until they run out. Once those are gone, +// there will be no more. +// +// An audio consumer needs to check the length, the +// ready status, and the aborted flag. +//////////////////////////////////////////////////////////////////// +int MovieAudioCursor:: +ready() const { + return 0x40000000; +} + diff --git a/panda/src/movies/movieAudioCursor.h b/panda/src/movies/movieAudioCursor.h index c1949c040a..d0e4fcb92d 100644 --- a/panda/src/movies/movieAudioCursor.h +++ b/panda/src/movies/movieAudioCursor.h @@ -50,20 +50,21 @@ PUBLISHED: INLINE double length() const; INLINE bool can_seek() const; INLINE bool can_seek_fast() const; - INLINE bool aborted() const; INLINE double tell() const; INLINE void skip_samples(int n); - INLINE int ready() const; + INLINE bool aborted() const; + virtual int ready() const; virtual void seek(double offset); + void read_samples(int n, Datagram *dg); + string read_samples(int n); public: virtual void read_samples(int n, PN_int16 *data); - + protected: PT(MovieAudio) _source; int _audio_rate; int _audio_channels; - int _ready; double _length; bool _can_seek; bool _can_seek_fast; diff --git a/panda/src/movies/movies_composite1.cxx b/panda/src/movies/movies_composite1.cxx index 39d57c5c94..9a8a9d6043 100644 --- a/panda/src/movies/movies_composite1.cxx +++ b/panda/src/movies/movies_composite1.cxx @@ -21,4 +21,7 @@ #include "webcamVideo.cxx" #include "webcamVideoDS.cxx" +#include "microphoneAudio.cxx" +#include "microphoneAudioDS.cxx" + #include "config_movies.cxx" diff --git a/panda/src/movies/userDataAudio.cxx b/panda/src/movies/userDataAudio.cxx index eb6f76eeb5..40f359e586 100644 --- a/panda/src/movies/userDataAudio.cxx +++ b/panda/src/movies/userDataAudio.cxx @@ -46,19 +46,6 @@ UserDataAudio:: ~UserDataAudio() { } -//////////////////////////////////////////////////////////////////// -// Function: UserDataAudio::update_cursor -// Access: Private -// Description: Make sure that the UserDataAudioCursor's ready -// and aborted status flags are correct. -//////////////////////////////////////////////////////////////////// -void UserDataAudio:: -update_cursor() { - if (_cursor == 0) return; - _cursor->_ready = _data.size(); - _cursor->_aborted = _aborted; -} - //////////////////////////////////////////////////////////////////// // Function: UserDataAudio::open // Access: Published, Virtual @@ -73,7 +60,6 @@ open() { return NULL; } _cursor = new UserDataAudioCursor(this); - update_cursor(); return _cursor; } @@ -87,48 +73,73 @@ open() { //////////////////////////////////////////////////////////////////// void UserDataAudio:: read_samples(int n, PN_int16 *data) { - int nread = n; - if (nread > (int)_data.size()) { - nread = _data.size(); - } - for (int i=0; i desired) avail = desired; + for (int i=0; iget_remaining_size() / (2 * _desired_channels); + if (n > maxlen) n = maxlen; + int words = n * _desired_channels; + for (int i=0; iget_int16()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: UserDataAudio::append +// Access: Published +// Description: Appends audio samples to the buffer from a +// string. The samples must be stored little-endian +// in the string. This is not particularly efficient, +// but it may be convenient to deal with samples in +// python. +//////////////////////////////////////////////////////////////////// +void UserDataAudio:: +append(const string &str) { + nassertv(!_aborted); + int samples = str.size() / (2 * _desired_channels); + int words = samples * _desired_channels; + for (int i=0; i_desired_channels; _can_seek = false; _can_seek_fast = false; - _ready = 0; _aborted = false; } @@ -60,4 +59,18 @@ void UserDataAudioCursor:: read_samples(int n, PN_int16 *data) { UserDataAudio *source = (UserDataAudio*)(MovieAudio*)_source; source->read_samples(n, data); + _samples_read += n; +} + +//////////////////////////////////////////////////////////////////// +// Function: UserDataAudioCursor::ready +// Access: Private +// Description: Returns the number of audio samples ready to be +// read. +//////////////////////////////////////////////////////////////////// +int UserDataAudioCursor:: +ready() const { + UserDataAudio *source = (UserDataAudio*)(MovieAudio*)_source; + ((UserDataAudioCursor*)this)->_aborted = source->_aborted; + return (source->_data.size()) / _audio_channels; } diff --git a/panda/src/movies/userDataAudioCursor.h b/panda/src/movies/userDataAudioCursor.h index 3a1ae9dcb6..300077fa59 100644 --- a/panda/src/movies/userDataAudioCursor.h +++ b/panda/src/movies/userDataAudioCursor.h @@ -38,6 +38,7 @@ PUBLISHED: public: virtual void read_samples(int n, PN_int16 *data); + virtual int ready() const; friend class UserDataAudio; public: diff --git a/panda/src/movies/webcamVideoDS.cxx b/panda/src/movies/webcamVideoDS.cxx index 9215d7bb10..773a66cf3b 100644 --- a/panda/src/movies/webcamVideoDS.cxx +++ b/panda/src/movies/webcamVideoDS.cxx @@ -1,4 +1,4 @@ -// Filename: webcamVideoDX.cxx +// Filename: webcamVideoDS.cxx // Created by: jyelon (01Nov2007) // //////////////////////////////////////////////////////////////////// @@ -102,7 +102,7 @@ public: return _type_handle; } static void init_type() { - MovieVideo::init_type(); + WebcamVideo::init_type(); register_type(_type_handle, "WebcamVideoDS", WebcamVideo::get_class_type()); } @@ -295,12 +295,6 @@ get_moniker_name(IMoniker *pMoniker) { return res; } -struct DeviceInfo { - string _name; - IMoniker *_moniker; - AM_MEDIA_TYPE *_media; -}; - //////////////////////////////////////////////////////////////////// // Function: WebcamVideoDS::add_device // Access: Public, Static