added streaming audio support

This commit is contained in:
Josh Yelon 2008-04-24 01:58:13 +00:00
parent 5547786f00
commit 16b6c060a8
15 changed files with 793 additions and 85 deletions

View File

@ -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

View File

@ -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();

View File

@ -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;
}

View File

@ -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<PT(MicrophoneAudio)> 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];
}

View File

@ -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<PT(MicrophoneAudio)> _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

View File

@ -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 <windows.h>
#include <mmsystem.h>
////////////////////////////////////////////////////////////////////
// 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 <AudioBuf> 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; i<ndevs; i++) {
WAVEINCAPS caps;
stat = waveInGetDevCaps(i, &caps, sizeof(caps));
if (stat != MMSYSERR_NOERROR) continue;
for (int chan=1; chan<=2; chan++) {
for (int fselect=0; freqs[fselect]; fselect++) {
WAVEFORMATEX format;
int freq = freqs[fselect];
format.wFormatTag = WAVE_FORMAT_PCM;
format.nChannels = chan;
format.nSamplesPerSec = freq;
format.nAvgBytesPerSec = freq * chan * 2;
format.nBlockAlign = 2 * chan;
format.wBitsPerSample = 16;
format.cbSize = 0;
stat = waveInOpen(NULL, i, &format, NULL, NULL, WAVE_FORMAT_QUERY);
if (stat == MMSYSERR_NOERROR) {
PT(MicrophoneAudioDS) p = new MicrophoneAudioDS();
ostringstream name;
name << "WaveIn: " << caps.szPname << " Chan:" << chan << " HZ:" << freq;
p->set_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

View File

@ -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);
}

View File

@ -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<n; i++) {
int desired = n * _audio_channels;
for (int i=0; i<desired; i++) {
data[i] = 0;
}
_samples_read += n;
}
////////////////////////////////////////////////////////////////////
// Function: MovieAudioCursor::read_samples
// Access: Published
// Description: Read audio samples from the stream into a
// Datagram. 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.
////////////////////////////////////////////////////////////////////
void MovieAudioCursor::
read_samples(int n, Datagram *dg) {
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<words; i++) {
dg->add_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<words; i++) {
PN_int16 word = tmp[i];
result.put((char)(word & 255));
result.put((char)((word>>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;
}

View File

@ -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;

View File

@ -21,4 +21,7 @@
#include "webcamVideo.cxx"
#include "webcamVideoDS.cxx"
#include "microphoneAudio.cxx"
#include "microphoneAudioDS.cxx"
#include "config_movies.cxx"

View File

@ -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<nread; i++) {
int ready = (_data.size() / _desired_channels);
int desired = n * _desired_channels;
int avail = ready * _desired_channels;
if (avail > desired) avail = desired;
for (int i=0; i<avail; i++) {
data[i] = _data[i];
}
for (int i=nread; i<n; i++) {
for (int i=avail; i<desired; i++) {
data[i] = 0;
}
for (int i=0; i<nread; i++) {
for (int i=0; i<avail; i++) {
_data.pop_front();
}
update_cursor();
}
////////////////////////////////////////////////////////////////////
// Function: UserDataAudio::append
// Access: Published
// Access: Public
// Description: Appends audio samples to the buffer.
////////////////////////////////////////////////////////////////////
void UserDataAudio::
append(PN_int16 *data, int len) {
append(PN_int16 *data, int n) {
nassertv(!_aborted);
for (int i=0; i<len; i++) {
int words = n * _desired_channels;
for (int i=0; i<words; i++) {
_data.push_back(data[i]);
}
update_cursor();
}
////////////////////////////////////////////////////////////////////
// Function: UserDataAudio::append
// Access: Published
// Description: Appends audio samples to the buffer.
// Description: Appends audio samples to the buffer from a
// datagram. This is intended to make it easy to
// send streaming raw audio over a network.
////////////////////////////////////////////////////////////////////
void UserDataAudio::
append(int val) {
append(DatagramIterator *src, int n) {
nassertv(!_aborted);
PN_int16 truncated = (PN_int16)val;
nassertv(truncated == val);
_data.push_back(truncated);
update_cursor();
int maxlen = src->get_remaining_size() / (2 * _desired_channels);
if (n > maxlen) n = maxlen;
int words = n * _desired_channels;
for (int i=0; i<words; i++) {
_data.push_back(src->get_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<words; i++) {
int c1 = ((unsigned char)str[i*2+0]);
int c2 = ((unsigned char)str[i*2+1]);
PN_int16 n = (c1 | (c2 << 8));
_data.push_back(n);
}
}
////////////////////////////////////////////////////////////////////
@ -140,5 +151,4 @@ append(int val) {
void UserDataAudio::
done() {
_aborted = true;
update_cursor();
}

View File

@ -20,6 +20,7 @@
#define USERDATAAUDIO_H
#include "movieAudio.h"
#include "datagramIterator.h"
class MovieAudioCursor;
class UserDataAudioCursor;
@ -35,8 +36,9 @@ class EXPCL_PANDA_MOVIES UserDataAudio : public MovieAudio {
virtual ~UserDataAudio();
virtual PT(MovieAudioCursor) open();
void append(PN_int16 *data, int len);
void append(int value); // Not fast enough, but useful for debugging.
void append(PN_int16 *data, int n);
void append(DatagramIterator *src, int len=0x40000000);
void append(const string &str);
void done(); // A promise not to write any more samples.
private:

View File

@ -33,7 +33,6 @@ UserDataAudioCursor(UserDataAudio *src) :
_audio_channels = src->_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;
}

View File

@ -38,6 +38,7 @@ PUBLISHED:
public:
virtual void read_samples(int n, PN_int16 *data);
virtual int ready() const;
friend class UserDataAudio;
public:

View File

@ -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