miles streaming

This commit is contained in:
David Rose 2007-08-01 18:52:35 +00:00
parent e51f0b2baf
commit 15f2e539d6
25 changed files with 2859 additions and 1299 deletions

View File

@ -209,7 +209,7 @@ setSpeakerSetup(SpeakerModeCategory cat) {
////////////////////////////////////////////////////////////////////
// Function: AudioManager::update()
// Access: Published
// Access: Published, Virtual
// Description: Must be called every frame. Failure to call this
// every frame could cause problems for some audio
// managers.
@ -296,7 +296,7 @@ float AudioManager::audio_3d_get_drop_off_factor() const {
////////////////////////////////////////////////////////////////////
// Function: AudioManager::get_dls_pathname
// Access: Public
// Access: Published, Static
// Description: Returns the full pathname to the DLS file, as
// specified by the Config.prc file, or the default for
// the current OS if appropriate. Returns empty string
@ -337,3 +337,23 @@ get_dls_pathname() {
return Filename();
#endif
}
////////////////////////////////////////////////////////////////////
// Function: AudioManager::output
// Access: Published, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void AudioManager::
output(ostream &out) const {
out << get_type();
}
////////////////////////////////////////////////////////////////////
// Function: AudioManager::write
// Access: Published, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void AudioManager::
write(ostream &out) const {
out << (*this) << "\n";
}

View File

@ -216,7 +216,10 @@ PUBLISHED:
virtual void audio_3d_set_drop_off_factor(float factor);
virtual float audio_3d_get_drop_off_factor() const;
Filename get_dls_pathname();
static Filename get_dls_pathname();
virtual void output(ostream &out) const;
virtual void write(ostream &out) const;
public:
static void register_AudioManager_creator(Create_AudioManager_proc* proc);
@ -251,4 +254,10 @@ private:
static TypeHandle _type_handle;
};
inline ostream &
operator << (ostream &out, const AudioManager &mgr) {
mgr.output(out);
return out;
}
#endif /* __AUDIO_MANAGER_H__ */

View File

@ -74,7 +74,7 @@ get_3d_max_distance() const {
}
////////////////////////////////////////////////////////////////////
// Function: AudioManager::add_dsp
// Function: AudioSound::add_dsp
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
@ -85,7 +85,7 @@ add_dsp(PT(AudioDSP) x) {
}
////////////////////////////////////////////////////////////////////
// Function: AudioManager::remove_dsp
// Function: AudioSound::remove_dsp
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
@ -97,7 +97,7 @@ remove_dsp(PT(AudioDSP) x) {
////////////////////////////////////////////////////////////////////
// Function: AudioManager::getSpeakerMix
// Function: AudioSound::getSpeakerMix
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
@ -108,7 +108,7 @@ get_speaker_mix(int speaker) {
}
////////////////////////////////////////////////////////////////////
// Function: AudioManager::setSpeakerMix
// Function: AudioSound::setSpeakerMix
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
@ -119,7 +119,7 @@ set_speaker_mix(float frontleft, float frontright, float center, float sub, floa
}
////////////////////////////////////////////////////////////////////
// Function: AudioManager::get_priority
// Function: AudioSound::get_priority
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
@ -130,7 +130,7 @@ get_priority() {
}
////////////////////////////////////////////////////////////////////
// Function: AudioManager::set_priority
// Function: AudioSound::set_priority
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
@ -140,3 +140,42 @@ set_priority(int priority) {
;
}
////////////////////////////////////////////////////////////////////
// Function: AudioSound::output
// Access: Published, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void AudioSound::
output(ostream &out) const {
out << get_type() << " " << get_name() << " " << status();
}
////////////////////////////////////////////////////////////////////
// Function: AudioSound::write
// Access: Published, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void AudioSound::
write(ostream &out) const {
out << (*this) << "\n";
}
////////////////////////////////////////////////////////////////////
// Function: AudioSound::SoundStatus::output operator
// Description:
////////////////////////////////////////////////////////////////////
ostream &
operator << (ostream &out, AudioSound::SoundStatus status) {
switch (status) {
case AudioSound::BAD:
return out << "BAD";
case AudioSound::READY:
return out << "READY";
case AudioSound::PLAYING:
return out << "PLAYING";
}
return out << "**invalid AudioSound::SoundStatus(" << (int)status << ")**";
}

View File

@ -28,7 +28,6 @@
class AudioManager;
class EXPCL_PANDA_AUDIO AudioSound : public TypedReferenceCount {
PUBLISHED:
virtual ~AudioSound();
@ -138,6 +137,9 @@ class EXPCL_PANDA_AUDIO AudioSound : public TypedReferenceCount {
enum SoundStatus { BAD, READY, PLAYING };
virtual SoundStatus status() const = 0;
virtual void output(ostream &out) const;
virtual void write(ostream &out) const;
protected:
AudioSound();
@ -161,4 +163,13 @@ class EXPCL_PANDA_AUDIO AudioSound : public TypedReferenceCount {
static TypeHandle _type_handle;
};
inline ostream &
operator << (ostream &out, const AudioSound &sound) {
sound.output(out);
return out;
}
EXPCL_PANDA_AUDIO ostream &
operator << (ostream &out, AudioSound::SoundStatus status);
#endif /* __AUDIOSOUND_H__ */

View File

@ -8,7 +8,7 @@
#define BUILD_TARGET $[HAVE_RAD_MSS]
#define USE_PACKAGES rad_mss
#define BUILDING_DLL BUILDING_MILES_AUDIO
#define LOCAL_LIBS audio event
#define LOCAL_LIBS audio event pipeline
#define WIN_SYS_LIBS $[WIN_SYS_LIBS] user32.lib advapi32.lib winmm.lib
#define COMBINED_SOURCES $[TARGET]_composite1.cxx
@ -16,10 +16,16 @@
#define SOURCES \
config_milesAudio.h \
milesAudioManager.h \
milesAudioSound.I milesAudioSound.h
milesAudioSound.I milesAudioSound.h \
milesAudioSample.I milesAudioSample.h \
milesAudioSequence.I milesAudioSequence.h \
milesAudioStream.I milesAudioStream.h \
globalMilesManager.I globalMilesManager.h
#define INCLUDED_SOURCES \
config_milesAudio.cxx milesAudioManager.cxx milesAudioSound.cxx
config_milesAudio.cxx milesAudioManager.cxx milesAudioSound.cxx \
milesAudioStream.cxx globalMilesManager.cxx milesAudioSample.cxx \
milesAudioSequence.cxx
#end lib_target

View File

@ -22,6 +22,9 @@
#include "config_milesAudio.h"
#include "milesAudioManager.h"
#include "milesAudioSound.h"
#include "milesAudioSample.h"
#include "milesAudioSequence.h"
#include "milesAudioStream.h"
#include "pandaSystem.h"
#include "dconfig.h"
@ -42,16 +45,26 @@ ConfigVariableInt miles_audio_expand_mp3_threshold
"work around problems with Miles being unable to correctly "
"report the length of, or seek within, a variable bit-rate encoded "
"MP3 file. Any MP3 file whose length in bytes is less than "
"this value will be expanded."));
"this value will be expanded. This only applies to files "
"within the miles-audio-preload-threshold."));
ConfigVariableInt miles_audio_calc_mp3_threshold
("miles-audio-calc-mp3-threshold", 1048576,
PRC_DESC("This is a second fallback for miles-audio-expand-mp3-threshold. "
"Any MP3 file whose length in bytes is less than this value "
"will have its length calculated on demand, by running through "
"the entire file first. This works around a Miles bug in "
"which variable bit-rate encoded MP3 files do not report an "
"accurate length."));
ConfigVariableInt miles_audio_preload_threshold
("miles-audio-preload-threshold", -1,
PRC_DESC("This is the last Miles fallback size, and should be no smaller "
"than both miles-audio-expand-mp3-threshold and "
"miles-audio-calc-mp3-threshold. Files that are smaller "
"than this number of bytes will be preloaded and kept "
"resident in memory, while files that are this size or larger "
"will be streamed from disk. Set this to -1 to preload "
"every file."));
ConfigVariableBool miles_audio_panda_threads
("miles-audio-panda-threads", true,
PRC_DESC("Set this true to service Miles background audio via Panda's "
"threading interface, instead of Miles' built-in threading "
"interface. This gives Panda more control over the threading, "
"and ensures better lock protection within Panda. This has "
"no meaning unless Panda is compiled with thread support."));
////////////////////////////////////////////////////////////////////
// Function: init_libMilesAudio
@ -73,6 +86,9 @@ init_libMilesAudio() {
MilesAudioManager::init_type();
MilesAudioSound::init_type();
MilesAudioSample::init_type();
MilesAudioSequence::init_type();
MilesAudioStream::init_type();
PandaSystem *ps = PandaSystem::get_global_ptr();
ps->add_system("Miles");

View File

@ -32,7 +32,8 @@ NotifyCategoryDecl(milesAudio, EXPCL_MILES_AUDIO, EXPTP_MILES_AUDIO);
extern ConfigVariableBool miles_audio_force_midi_reset;
extern ConfigVariableInt miles_audio_expand_mp3_threshold;
extern ConfigVariableInt miles_audio_calc_mp3_threshold;
extern ConfigVariableInt miles_audio_preload_threshold;
extern ConfigVariableBool miles_audio_panda_threads;
extern EXPCL_MILES_AUDIO void init_libMilesAudio();

View File

@ -0,0 +1,29 @@
// Filename: globalMilesManager.I
// Created by: drose (30Jul07)
//
////////////////////////////////////////////////////////////////////
//
// 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: GlobalMilesManager::is_open
// Access: Public
// Description: Returns true if the Miles Sound System is open (and
// active), false if it is not.
////////////////////////////////////////////////////////////////////
INLINE bool GlobalMilesManager::
is_open() const {
return _is_open;
}

View File

@ -0,0 +1,489 @@
// Filename: globalMilesManager.cxx
// Created by: drose (26Jul07)
//
////////////////////////////////////////////////////////////////////
//
// 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 .
//
////////////////////////////////////////////////////////////////////
#include "globalMilesManager.h"
#ifdef HAVE_RAD_MSS //[
#include "mutexHolder.h"
#include "milesAudioManager.h"
#include "milesAudioSample.h"
#include "milesAudioSequence.h"
GlobalMilesManager *GlobalMilesManager::_global_ptr;
////////////////////////////////////////////////////////////////////
// Function: GlobalMilesManager::Constructor
// Access: Private
// Description:
////////////////////////////////////////////////////////////////////
GlobalMilesManager::
GlobalMilesManager() :
_managers_lock("GlobalMilesManager::_managers_lock"),
_samples_lock("GlobalMilesManager::_samples_lock"),
_sequences_lock("GlobalMilesManager::_sequences_lock")
{
_digital_driver = 0;
_midi_driver = 0;
_dls_device = 0;
_dls_file = 0;
_is_open = false;
}
////////////////////////////////////////////////////////////////////
// Function: GlobalMilesManager::add_manager
// Access: Public
// Description: Records a new MilesAudioManager in the world. This
// will open the Miles API when the first audio manager
// is added.
////////////////////////////////////////////////////////////////////
void GlobalMilesManager::
add_manager(MilesAudioManager *manager) {
MutexHolder holder(_managers_lock);
_managers.insert(manager);
if (!_is_open) {
open_api();
}
}
////////////////////////////////////////////////////////////////////
// Function: GlobalMilesManager::remove_manager
// Access: Public
// Description: Records that a MilesAudioManager is destructing.
// This will clsoe the Miles API when the last audio
// manager is removed.
////////////////////////////////////////////////////////////////////
void GlobalMilesManager::
remove_manager(MilesAudioManager *manager) {
MutexHolder holder(_managers_lock);
_managers.erase(manager);
if (_managers.empty() && _is_open) {
close_api();
}
}
////////////////////////////////////////////////////////////////////
// Function: GlobalMilesManager::cleanup
// Access: Public
// Description: Calls cleanup() on all MilesAudioManagers, to cause a
// clean shutdown.
////////////////////////////////////////////////////////////////////
void GlobalMilesManager::
cleanup() {
MutexHolder holder(_managers_lock);
Managers::iterator mi;
for (mi = _managers.begin(); mi != _managers.end(); ++mi) {
(*mi)->cleanup();
}
}
////////////////////////////////////////////////////////////////////
// Function: GlobalMilesManager::get_sample
// Access: Public
// Description: Gets a sample handle from the global pool for the
// digital output device, to be used with the indicated
// AudioSound.
//
// If successful, sets the sample handle and the index
// (which should later be used to release the sample)
// and returns true. If unsuccessful (because there are
// no more available handles), returns false.
//
// This is a very limited resource; you should only get
// a sample just before playing a sound.
////////////////////////////////////////////////////////////////////
bool GlobalMilesManager::
get_sample(HSAMPLE &sample, size_t &index, MilesAudioSample *sound) {
MutexHolder holder(_samples_lock);
for (size_t i = 0; i < _samples.size(); ++i) {
SampleData &smp = _samples[i];
if (AIL_sample_status(smp._sample) == SMP_DONE) {
if (smp._sound != NULL) {
// Tell the last sound that was using this sample that it's
// done now.
smp._sound->internal_stop();
}
smp._sound = sound;
sample = smp._sample;
index = i;
return true;
}
}
// No more already-allocated samples; get a new one from the system.
sample = AIL_allocate_sample_handle(_digital_driver);
if (sample == 0) {
return false;
}
AIL_init_sample(sample, DIG_F_STEREO_16, 0);
index = _samples.size();
SampleData smp;
smp._sound = sound;
smp._sample = sample;
_samples.push_back(smp);
return true;
}
////////////////////////////////////////////////////////////////////
// Function: GlobalMilesManager::release_sample
// Access: Public
// Description: Indicates that the indicated AudioSound no longer
// needs this sample.
////////////////////////////////////////////////////////////////////
void GlobalMilesManager::
release_sample(size_t index, MilesAudioSample *sound) {
MutexHolder holder(_samples_lock);
nassertv(index < _samples.size());
SampleData &smp = _samples[index];
if (smp._sound == sound) {
smp._sound = NULL;
}
}
////////////////////////////////////////////////////////////////////
// Function: GlobalMilesManager::get_sequence
// Access: Public
// Description: Gets a sequence handle from the global pool for the
// digital output device, to be used with the indicated
// AudioSound.
//
// If successful, sets the sequence handle and the index
// (which should later be used to release the sequence)
// and returns true. If unsuccessful (because there are
// no more available handles), returns false.
//
// This is a very limited resource; you should only get
// a sequence just before playing a sound.
////////////////////////////////////////////////////////////////////
bool GlobalMilesManager::
get_sequence(HSEQUENCE &sequence, size_t &index, MilesAudioSequence *sound) {
MutexHolder holder(_sequences_lock);
for (size_t i = 0; i < _sequences.size(); ++i) {
SequenceData &seq = _sequences[i];
if (AIL_sequence_status(seq._sequence) == SEQ_DONE) {
if (seq._sound != NULL) {
// Tell the last sound that was using this sequence that it's
// done now.
seq._sound->internal_stop();
}
seq._sound = sound;
sequence = seq._sequence;
index = i;
return true;
}
}
// No more already-allocated sequences; get a new one from the system.
sequence = AIL_allocate_sequence_handle(_midi_driver);
if (sequence == 0) {
return false;
}
index = _sequences.size();
SequenceData seq;
seq._sound = sound;
seq._sequence = sequence;
_sequences.push_back(seq);
return true;
}
////////////////////////////////////////////////////////////////////
// Function: GlobalMilesManager::release_sequence
// Access: Public
// Description: Indicates that the indicated AudioSound no longer
// needs this sequence.
////////////////////////////////////////////////////////////////////
void GlobalMilesManager::
release_sequence(size_t index, MilesAudioSequence *sound) {
MutexHolder holder(_sequences_lock);
nassertv(index < _sequences.size());
SequenceData &seq = _sequences[index];
if (seq._sound == sound) {
seq._sound = NULL;
}
}
////////////////////////////////////////////////////////////////////
// Function: GlobalMilesManager::force_midi_reset
// Access: Public
// Description: Sometimes Miles seems to leave midi notes hanging,
// even after stop is called, so call this method to
// perform an explicit reset using winMM.dll calls, just
// to ensure silence.
////////////////////////////////////////////////////////////////////
void GlobalMilesManager::
force_midi_reset() {
if (!miles_audio_force_midi_reset) {
audio_debug("MilesAudioManager::skipping force_midi_reset");
return;
}
audio_debug("MilesAudioManager::force_midi_reset");
#ifdef WIN32
if ((_midi_driver!=NULL) && (_midi_driver->deviceid != MIDI_NULL_DRIVER) && (_midi_driver->hMidiOut != NULL)) {
audio_debug("MilesAudioManager::calling midiOutReset");
midiOutReset(_midi_driver->hMidiOut);
}
#endif
}
////////////////////////////////////////////////////////////////////
// Function: GlobalMilesManager::get_global_ptr
// Access: Public, Static
// Description: Returns the pointer to the one GlobalMilesManager
// object.
////////////////////////////////////////////////////////////////////
GlobalMilesManager *GlobalMilesManager::
get_global_ptr() {
if (_global_ptr == NULL) {
_global_ptr = new GlobalMilesManager;
}
return _global_ptr;
}
////////////////////////////////////////////////////////////////////
// Function: GlobalMilesManager::open_api
// Access: Private
// Description: Called internally to initialize the Miles API.
////////////////////////////////////////////////////////////////////
void GlobalMilesManager::
open_api() {
audio_debug("GlobalMilesManager::open_api()")
nassertv(!_is_open);
bool use_digital = (audio_play_wave || audio_play_mp3);
if (audio_play_midi && audio_software_midi) {
use_digital = true;
}
#ifdef IS_OSX
audio_software_midi = true;
#endif
audio_debug(" use_digital="<<use_digital);
audio_debug(" audio_play_midi="<<audio_play_midi);
audio_debug(" audio_software_midi="<<audio_software_midi);
audio_debug(" audio_output_rate="<<audio_output_rate);
audio_debug(" audio_output_bits="<<audio_output_bits);
audio_debug(" audio_output_channels="<<audio_output_channels);
audio_debug(" audio_software_midi="<<audio_software_midi);
#if !defined(NDEBUG) && defined(AIL_MSS_version) //[
char version[8];
AIL_MSS_version(version, 8);
audio_debug(" Mss32.dll Version: "<<version);
#endif //]
if (!AIL_startup()) {
milesAudio_cat.warning()
<< "Miles Sound System already initialized!\n";
}
AIL_set_file_callbacks(open_callback, close_callback,
seek_callback, read_callback);
if (use_digital) {
_digital_driver =
AIL_open_digital_driver(audio_output_rate, audio_output_bits,
audio_output_channels, 0);
}
if (audio_play_midi) {
if (audio_software_midi) {
_midi_driver = AIL_open_XMIDI_driver(AIL_OPEN_XMIDI_NULL_DRIVER);
// Load the downloadable sounds file:
_dls_device = AIL_DLS_open(_midi_driver, _digital_driver, NULL, 0,
audio_output_rate, audio_output_bits,
audio_output_channels);
Filename dls_pathname = AudioManager::get_dls_pathname();
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
vfs->resolve_filename(dls_pathname, get_sound_path()) ||
vfs->resolve_filename(dls_pathname, get_model_path());
_dls_data.clear();
PT(VirtualFile) file = vfs->get_file(dls_pathname);
if (file == (VirtualFile *)NULL) {
milesAudio_cat.warning()
<< "DLS file does not exist: " << dls_pathname << "\n";
} else if (!file->read_file(_dls_data, true)) {
milesAudio_cat.warning()
<< "Could not read DLS file: " << dls_pathname << "\n";
} else if (_dls_data.empty()) {
milesAudio_cat.warning()
<< "DLS file is empty: " << dls_pathname << "\n";
} else {
_dls_file = AIL_DLS_load_memory(_dls_device, &_dls_data[0], 0);
}
if (_dls_file == 0) {
audio_error(" Could not get DLS file, switching to hardware MIDI.");
AIL_DLS_close(_dls_device, 0);
_dls_device = 0;
AIL_close_XMIDI_driver(_midi_driver);
_midi_driver = AIL_open_XMIDI_driver(0);
} else {
audio_info(" using Miles software midi");
}
} else {
_midi_driver = AIL_open_XMIDI_driver(0);
audio_info(" using Miles hardware midi");
}
}
_is_open = true;
}
////////////////////////////////////////////////////////////////////
// Function: GlobalMilesManager::close_api
// Access: Private
// Description: Called internally to shut down the Miles API.
////////////////////////////////////////////////////////////////////
void GlobalMilesManager::
close_api() {
audio_debug("GlobalMilesManager::close_api()")
nassertv(_is_open);
Samples::iterator si;
for (si = _samples.begin(); si != _samples.end(); ++si) {
SampleData &smp = (*si);
AIL_release_sample_handle(smp._sample);
}
_samples.clear();
Sequences::iterator qi;
for (qi = _sequences.begin(); qi != _sequences.end(); ++qi) {
SequenceData &smp = (*qi);
AIL_release_sequence_handle(smp._sequence);
}
_sequences.clear();
if (_dls_file != 0) {
AIL_DLS_unload(_dls_device, _dls_file);
_dls_file = 0;
}
if (_dls_device != 0) {
AIL_DLS_close(_dls_device, 0);
_dls_device = 0;
}
if (_midi_driver != 0) {
AIL_close_XMIDI_driver(_midi_driver);
_midi_driver = 0;
}
if (_digital_driver != 0) {
AIL_close_digital_driver(_digital_driver);
_digital_driver = 0;
}
AIL_shutdown();
_is_open = false;
}
////////////////////////////////////////////////////////////////////
// Function: GlobalMilesManager::open_callback
// Access: Private, Static
// Description: This callback function is given to Miles to handle
// file I/O via the Panda VFS. It's only used to
// implemented streaming audio files, since in all other
// cases we open files directly.
////////////////////////////////////////////////////////////////////
U32 AILCALLBACK GlobalMilesManager::
open_callback(char const *filename, UINTa *file_handle) {
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
istream *strm = vfs->open_read_file(Filename::binary_filename(filename), true);
if (strm == NULL) {
// Failure.
return 0;
}
// Success.
(*file_handle) = (UINTa)strm;
return 1;
}
////////////////////////////////////////////////////////////////////
// Function: GlobalMilesManager::close_callback
// Access: Private, Static
// Description: This callback function is given to Miles to handle
// file I/O via the Panda VFS.
////////////////////////////////////////////////////////////////////
void AILCALLBACK GlobalMilesManager::
close_callback(UINTa file_handle) {
istream *strm = (istream *)file_handle;
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
vfs->close_read_file(strm);
}
////////////////////////////////////////////////////////////////////
// Function: GlobalMilesManager::seek_callback
// Access: Private, Static
// Description: This callback function is given to Miles to handle
// file I/O via the Panda VFS.
////////////////////////////////////////////////////////////////////
S32 AILCALLBACK GlobalMilesManager::
seek_callback(UINTa file_handle, S32 offset, U32 type) {
istream *strm = (istream *)file_handle;
strm->clear();
switch (type) {
case AIL_FILE_SEEK_BEGIN:
strm->seekg(offset, ios::beg);
break;
case AIL_FILE_SEEK_CURRENT:
strm->seekg(offset, ios::cur);
break;
case AIL_FILE_SEEK_END:
strm->seekg(offset, ios::end);
break;
}
return strm->tellg();
}
////////////////////////////////////////////////////////////////////
// Function: GlobalMilesManager::read_callback
// Access: Private, Static
// Description: This callback function is given to Miles to handle
// file I/O via the Panda VFS.
////////////////////////////////////////////////////////////////////
U32 AILCALLBACK GlobalMilesManager::
read_callback(UINTa file_handle, void *buffer, U32 bytes) {
istream *strm = (istream *)file_handle;
strm->read((char *)buffer, bytes);
return strm->gcount();
}
#endif //]

View File

@ -0,0 +1,114 @@
// Filename: globalMilesManager.h
// Created by: drose (26Jul07)
//
////////////////////////////////////////////////////////////////////
//
// 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 GLOBALMILESMANAGER_H
#define GLOBALMILESMANAGER_H
#include "pandabase.h"
#ifdef HAVE_RAD_MSS //[
#include "mss.h"
#include "pset.h"
#include "pmutex.h"
class MilesAudioSample;
class MilesAudioSequence;
////////////////////////////////////////////////////////////////////
// Class : GlobalMilesManager
// Description : This is a wrapper around the parts of the Miles API
// that should only be created once. This represents
// the global data common to all MilesAudioManagers.
////////////////////////////////////////////////////////////////////
class EXPCL_MILES_AUDIO GlobalMilesManager {
private:
GlobalMilesManager();
public:
void add_manager(MilesAudioManager *manager);
void remove_manager(MilesAudioManager *manager);
void cleanup();
INLINE bool is_open() const;
bool get_sample(HSAMPLE &sample, size_t &index, MilesAudioSample *sound);
void release_sample(size_t index, MilesAudioSample *sound);
bool get_sequence(HSEQUENCE &sequence, size_t &index, MilesAudioSequence *sound);
void release_sequence(size_t index, MilesAudioSequence *sound);
void force_midi_reset();
static GlobalMilesManager *get_global_ptr();
public:
HDIGDRIVER _digital_driver;
HMDIDRIVER _midi_driver;
// For software MIDI:
HDLSDEVICE _dls_device;
HDLSFILEID _dls_file;
pvector<unsigned char> _dls_data;
private:
void open_api();
void close_api();
static U32 AILCALLBACK open_callback(char const *filename, UINTa *file_handle);
static void AILCALLBACK close_callback(UINTa file_handle);
static S32 AILCALLBACK seek_callback(UINTa file_handle, S32 offset, U32 type);
static U32 AILCALLBACK read_callback(UINTa file_handle, void *buffer, U32 bytes);
private:
bool _is_open;
typedef pset<MilesAudioManager *> Managers;
Managers _managers;
Mutex _managers_lock;
class SampleData {
public:
HSAMPLE _sample;
MilesAudioSample *_sound;
};
typedef pvector<SampleData> Samples;
Samples _samples;
Mutex _samples_lock;
class SequenceData {
public:
HSEQUENCE _sequence;
MilesAudioSequence *_sound;
};
typedef pvector<SequenceData> Sequences;
Sequences _sequences;
Mutex _sequences_lock;
static GlobalMilesManager *_global_ptr;
};
#include "globalMilesManager.I"
#endif //]
#endif

File diff suppressed because it is too large Load Diff

View File

@ -29,6 +29,9 @@
#include "pmap.h"
#include "pdeque.h"
#include "pvector.h"
#include "thread.h"
#include "pmutex.h"
#include "conditionVar.h"
class MilesAudioSound;
@ -37,35 +40,69 @@ public:
// See AudioManager.h for documentation.
MilesAudioManager();
~MilesAudioManager();
virtual ~MilesAudioManager();
virtual void shutdown();
bool is_valid();
virtual bool is_valid();
virtual PT(AudioSound) get_sound(const string &file_name, bool positional = false);
void uncache_sound(const string& file_name);
void clear_cache();
void set_cache_limit(unsigned int count);
unsigned int get_cache_limit() const;
virtual void uncache_sound(const string &file_name);
virtual void clear_cache();
virtual void set_cache_limit(unsigned int count);
virtual unsigned int get_cache_limit() const;
void set_volume(float volume);
float get_volume() const;
virtual void set_volume(float volume);
virtual float get_volume() const;
void set_play_rate(float play_rate);
float get_play_rate() const;
void set_active(bool active);
bool get_active() const;
virtual void set_active(bool active);
virtual bool get_active() const;
void set_concurrent_sound_limit(unsigned int limit = 0);
unsigned int get_concurrent_sound_limit() const;
virtual void set_concurrent_sound_limit(unsigned int limit = 0);
virtual unsigned int get_concurrent_sound_limit() const;
void reduce_sounds_playing_to(unsigned int count);
virtual void reduce_sounds_playing_to(unsigned int count);
void stop_all_sounds();
virtual void stop_all_sounds();
virtual void update();
// Tell the manager that the sound dtor was called.
void release_sound(MilesAudioSound *audioSound);
void cleanup();
virtual void output(ostream &out) const;
virtual void write(ostream &out) const;
private:
void start_service_stream(HSTREAM stream);
void stop_service_stream(HSTREAM stream);
void most_recently_used(const string &path);
void uncache_a_sound();
void starting_sound(MilesAudioSound *audio);
void stopping_sound(MilesAudioSound *audio);
class SoundData;
PT(SoundData) load(const Filename &file_name);
void thread_main(volatile bool &keep_running);
void do_service_streams();
private:
class StreamThread : public Thread {
public:
StreamThread(MilesAudioManager *mgr);
virtual void thread_main();
MilesAudioManager *_mgr;
volatile bool _keep_running;
};
// The sound cache:
class SoundData : public ReferenceCount {
public:
@ -73,8 +110,7 @@ private:
~SoundData();
float get_length();
string _basename;
HAUDIO _audio;
Filename _basename;
S32 _file_type;
pvector<unsigned char> _raw_data;
bool _has_length;
@ -100,38 +136,16 @@ private:
bool _active;
int _cache_limit;
bool _cleanup_required;
// keep a count for startup and shutdown:
static int _active_managers;
static bool _miles_active;
unsigned int _concurrent_sound_limit;
bool _is_valid;
bool _hasMidiSounds;
// Optional Downloadable Sound field for software midi
static HDLSFILEID _dls_field;
typedef pset<MilesAudioManager *> Managers;
static Managers *_managers;
PT(SoundData) load(Filename file_name);
// Tell the manager that the sound dtor was called.
void release_sound(MilesAudioSound* audioSound);
void most_recently_used(const string& path);
void uncache_a_sound();
void starting_sound(MilesAudioSound* audio);
void stopping_sound(MilesAudioSound* audio);
// get the default dls file path:
void get_gm_file_path(string& result);
void force_midi_reset();
void cleanup();
friend class MilesAudioSound;
typedef pvector<HSTREAM> Streams;
PT(StreamThread) _stream_thread;
Streams _streams;
Mutex _streams_lock;
ConditionVar _streams_cvar;
public:
static TypeHandle get_class_type() {
@ -149,6 +163,11 @@ public:
private:
static TypeHandle _type_handle;
friend class MilesAudioSound;
friend class MilesAudioSample;
friend class MilesAudioSequence;
friend class MilesAudioStream;
};
EXPCL_MILES_AUDIO PT(AudioManager) Create_AudioManager();

View File

@ -0,0 +1,24 @@
// Filename: milesAudioSample.I
// Created by: skyler (June 6, 2001)
// Prior system by: cary
//
////////////////////////////////////////////////////////////////////
//
// 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 .
//
////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,309 @@
// Filename: milesAudioSample.cxx
// Created by: skyler (June 6, 2001)
//
////////////////////////////////////////////////////////////////////
//
// 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 .
//
////////////////////////////////////////////////////////////////////
#include "milesAudioSample.h"
#ifdef HAVE_RAD_MSS //[
#include "milesAudioManager.h"
TypeHandle MilesAudioSample::_type_handle;
#undef miles_audio_debug
#ifndef NDEBUG //[
#define miles_audio_debug(x) \
audio_debug("MilesAudioSample \""<<get_name()<<"\" "<< x )
#else //][
#define miles_audio_debug(x) ((void)0)
#endif //]
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSample::Constructor
// Access: Private
// Description: This constructor is called only by the
// MilesAudioManager.
////////////////////////////////////////////////////////////////////
MilesAudioSample::
MilesAudioSample(MilesAudioManager *manager, MilesAudioManager::SoundData *sd,
const string &file_name) :
MilesAudioSound(manager, file_name),
_sd(sd)
{
nassertv(sd != NULL);
audio_debug("MilesAudioSample(manager=0x"<<(void*)&manager
<<", sd=0x"<<(void*)sd<<", file_name="<<file_name<<")");
_sample = 0;
_sample_index = 0;
_original_playback_rate = 1.0f;
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSample::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
MilesAudioSample::
~MilesAudioSample() {
miles_audio_debug("~MilesAudioSample()");
cleanup();
_manager->release_sound(this);
miles_audio_debug("~MilesAudioSample() done");
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSample::play
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioSample::
play() {
miles_audio_debug("play()");
if (_active) {
if (_sd->_raw_data.empty()) {
milesAudio_cat.warning()
<< "Could not play " << _file_name << ": no data\n";
} else {
stop();
_manager->starting_sound(this);
nassertv(_sample == 0);
GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
if (!mgr->get_sample(_sample, _sample_index, this)){
milesAudio_cat.warning()
<< "Could not play " << _file_name << ": too many open samples\n";
_sample = 0;
} else {
AIL_set_named_sample_file(_sample, _sd->_basename.c_str(),
&_sd->_raw_data[0], _sd->_raw_data.size(),
0);
_original_playback_rate = AIL_sample_playback_rate(_sample);
set_volume(_volume);
set_play_rate(_play_rate);
AIL_set_sample_loop_count(_sample, _loop_count);
AIL_start_sample(_sample);
}
}
} else {
// In case _loop_count gets set to forever (zero):
audio_debug(" paused "<<_file_name );
_paused = true;
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSample::stop
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioSample::
stop() {
miles_audio_debug("stop()");
_manager->stopping_sound(this);
// The _paused flag should not be cleared here. _paused is not like
// the Pause button on a cd/dvd player. It is used as a flag to say
// that it was looping when it was set inactive. There is no need to
// make this symmetrical with play(). set_active() is the 'owner' of
// _paused. play() accesses _paused to help in the situation where
// someone calls play on an inactive sound().
if (_sample != 0) {
AIL_end_sample(_sample);
GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
mgr->release_sample(_sample_index, this);
_sample = 0;
_sample_index = 0;
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSample::set_time
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioSample::
set_time(float time) {
miles_audio_debug("set_time(time="<<time<<")");
if (_sample != 0) {
// Ensure we don't inadvertently run off the end of the sound.
float max_time = length();
if (time > max_time) {
milesAudio_cat.warning()
<< "set_time(" << time << ") requested for sound of length "
<< max_time << "\n";
time = max_time;
}
S32 time_ms = (S32)(1000.0f * time);
AIL_set_sample_ms_position(_sample, time_ms);
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSample::get_time
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
float MilesAudioSample::
get_time() const {
if (_sample == 0) {
return 0.0f;
}
S32 current_ms;
AIL_sample_ms_position(_sample, NULL, &current_ms);
float time = float(current_ms * 0.001f);
return time;
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSample::set_volume
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioSample::
set_volume(float volume) {
miles_audio_debug("set_volume(volume="<<volume<<")");
// Set the volume even if our volume is not changing, because the
// MilesAudioManager will call set_volume() when *its* volume
// changes.
// Set the volume:
_volume = volume;
if (_sample != 0) {
volume *= _manager->get_volume();
// Change to Miles volume, range 0 to 1.0:
F32 milesVolume = volume;
milesVolume = min(milesVolume, 1.0f);
milesVolume = max(milesVolume, 0.0f);
// Convert balance of -1.0..1.0 to 0-1.0:
F32 milesBalance = (F32)((_balance + 1.0f) * 0.5f);
AIL_set_sample_volume_pan(_sample, milesVolume, milesBalance);
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSample::set_balance
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioSample::
set_balance(float balance_right) {
miles_audio_debug("set_balance(balance_right="<<balance_right<<")");
_balance = balance_right;
// Call set_volume to effect the change:
set_volume(_volume);
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSample::set_play_rate
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioSample::
set_play_rate(float play_rate) {
miles_audio_debug("set_play_rate(play_rate="<<play_rate<<")");
// Set the play_rate:
_play_rate = play_rate;
if (_sample != 0) {
play_rate *= _manager->get_play_rate();
// wave and mp3 use sample rate (e.g. 44100)
S32 speed = (S32)(play_rate * (float)_original_playback_rate);
AIL_set_sample_playback_rate(_sample, speed);
audio_debug(" play_rate for this wav or mp3 is now " << speed);
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSample::length
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
float MilesAudioSample::
length() const {
return _sd->get_length();
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSample::status
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
AudioSound::SoundStatus MilesAudioSample::
status() const {
if (_sample == 0) {
return AudioSound::READY;
}
switch (AIL_sample_status(_sample)) {
case SMP_DONE:
case SMP_STOPPED:
case SMP_FREE:
return AudioSound::READY;
case SMP_PLAYING:
case SMP_PLAYINGBUTRELEASED:
return AudioSound::PLAYING;
default:
return AudioSound::BAD;
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSample::cleanup
// Access: Public, Virtual
// Description: Stops the sound from playing and releases any
// associated resources, in preparation for releasing
// the sound or shutting down the sound system.
////////////////////////////////////////////////////////////////////
void MilesAudioSample::
cleanup() {
stop();
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSample::internal_stop
// Access: Private
// Description: Called by the GlobalMilesManager when it is detected
// that this particular sound has already stopped, and
// its sample handle will be recycled.
////////////////////////////////////////////////////////////////////
void MilesAudioSample::
internal_stop() {
_sample = 0;
_sample_index = 0;
}
#endif //]

View File

@ -0,0 +1,93 @@
// Filename: milesAudioSample.h
// Created by: skyler (June 6, 2001)
// Prior system by: cary
//
////////////////////////////////////////////////////////////////////
//
// 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 MILESAUDIOSAMPLE_H
#define MILESAUDIOSAMPLE_H
#include "pandabase.h"
#ifdef HAVE_RAD_MSS //[
#include "milesAudioSound.h"
#include "milesAudioManager.h"
#include "mss.h"
////////////////////////////////////////////////////////////////////
// Class : MilesAudioSample
// Description : A sound file, such as a WAV or MP3 file, that is
// preloaded into memory and played from memory.
////////////////////////////////////////////////////////////////////
class EXPCL_MILES_AUDIO MilesAudioSample : public MilesAudioSound {
private:
MilesAudioSample(MilesAudioManager *manager,
MilesAudioManager::SoundData *sd,
const string &file_name);
public:
virtual ~MilesAudioSample();
virtual void play();
virtual void stop();
virtual void set_time(float time=0.0f);
virtual float get_time() const;
virtual void set_volume(float volume=1.0f);
virtual void set_balance(float balance_right=0.0f);
virtual void set_play_rate(float play_rate=1.0f);
virtual float length() const;
virtual AudioSound::SoundStatus status() const;
virtual void cleanup();
private:
void internal_stop();
PT(MilesAudioManager::SoundData) _sd;
HSAMPLE _sample;
size_t _sample_index;
S32 _original_playback_rate;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
MilesAudioSound::init_type();
register_type(_type_handle, "MilesAudioSample",
MilesAudioSound::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;
friend class GlobalMilesManager;
friend class MilesAudioManager;
};
#include "milesAudioSample.I"
#endif //]
#endif /* MILESAUDIOSAMPLE_H */

View File

@ -0,0 +1,18 @@
// Filename: milesAudioSequence.I
// Created by: drose (31Jul07)
//
////////////////////////////////////////////////////////////////////
//
// 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 .
//
////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,299 @@
// Filename: milesAudioSequence.cxx
// Created by: drose (31Jul07)
//
////////////////////////////////////////////////////////////////////
//
// 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 .
//
////////////////////////////////////////////////////////////////////
#include "milesAudioSequence.h"
#ifdef HAVE_RAD_MSS //[
#include "milesAudioManager.h"
TypeHandle MilesAudioSequence::_type_handle;
#undef miles_audio_debug
#ifndef NDEBUG //[
#define miles_audio_debug(x) \
audio_debug("MilesAudioSequence \""<<get_name()<<"\" "<< x )
#else //][
#define miles_audio_debug(x) ((void)0)
#endif //]
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSequence::Constructor
// Access: Private
// Description: This constructor is called only by the
// MilesAudioManager.
////////////////////////////////////////////////////////////////////
MilesAudioSequence::
MilesAudioSequence(MilesAudioManager *manager, MilesAudioManager::SoundData *sd,
const string &file_name) :
MilesAudioSound(manager, file_name),
_sd(sd)
{
nassertv(sd != NULL);
audio_debug("MilesAudioSequence(manager=0x"<<(void*)&manager
<<", sd=0x"<<(void*)sd<<", file_name="<<file_name<<")");
_sequence = 0;
_sequence_index = 0;
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSequence::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
MilesAudioSequence::
~MilesAudioSequence() {
miles_audio_debug("~MilesAudioSequence()");
cleanup();
_manager->release_sound(this);
miles_audio_debug("~MilesAudioSequence() done");
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSequence::play
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioSequence::
play() {
miles_audio_debug("play()");
if (_active) {
stop();
if (_sd->_raw_data.empty()) {
milesAudio_cat.warning()
<< "Could not play " << _file_name << ": no data\n";
} else {
_manager->starting_sound(this);
nassertv(_sequence == 0);
GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
if (!mgr->get_sequence(_sequence, _sequence_index, this)){
milesAudio_cat.warning()
<< "Could not play " << _file_name << ": too many open sequences\n";
_sequence = 0;
} else {
AIL_init_sequence(_sequence, &_sd->_raw_data[0], 0);
set_volume(_volume);
set_play_rate(_play_rate);
AIL_set_sequence_loop_count(_sequence, _loop_count);
AIL_start_sequence(_sequence);
}
}
} else {
// In case _loop_count gets set to forever (zero):
audio_debug(" paused "<<_file_name );
_paused = true;
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSequence::stop
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioSequence::
stop() {
miles_audio_debug("stop()");
_manager->stopping_sound(this);
// The _paused flag should not be cleared here. _paused is not like
// the Pause button on a cd/dvd player. It is used as a flag to say
// that it was looping when it was set inactive. There is no need to
// make this symmetrical with play(). set_active() is the 'owner' of
// _paused. play() accesses _paused to help in the situation where
// someone calls play on an inactive sound().
if (_sequence != 0) {
AIL_end_sequence(_sequence);
GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
mgr->release_sequence(_sequence_index, this);
_sequence = 0;
_sequence_index = 0;
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSequence::set_time
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioSequence::
set_time(float time) {
miles_audio_debug("set_time(time="<<time<<")");
if (_sequence != 0) {
S32 time_ms = (S32)(1000.0f * time);
// Ensure we don't inadvertently run off the end of the sound.
S32 length_ms;
AIL_sequence_ms_position(_sequence, &length_ms, NULL);
time_ms = min(time_ms, length_ms);
AIL_set_sequence_ms_position(_sequence, time_ms);
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSequence::get_time
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
float MilesAudioSequence::
get_time() const {
if (_sequence == 0) {
return 0.0f;
}
S32 current_ms;
AIL_sequence_ms_position(_sequence, NULL, &current_ms);
float time = float(current_ms * 0.001f);
return time;
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSequence::set_volume
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioSequence::
set_volume(float volume) {
miles_audio_debug("set_volume(volume="<<volume<<")");
// Set the volume even if our volume is not changing, because the
// MilesAudioManager will call set_volume() when *its* volume
// changes.
// Set the volume:
_volume = volume;
if (_sequence != 0) {
volume *= _manager->get_volume();
// Change to Miles volume, range 0 to 127:
S32 milesVolume = (S32)(volume * 127.0f);
milesVolume = min(milesVolume, 127);
milesVolume = max(milesVolume, 0);
AIL_set_sequence_volume(_sequence, milesVolume, 0);
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSequence::set_balance
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioSequence::
set_balance(float balance_right) {
miles_audio_debug("set_balance(balance_right="<<balance_right<<")");
_balance = balance_right;
// Balance has no effect on a MIDI file.
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSequence::set_play_rate
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioSequence::
set_play_rate(float play_rate) {
miles_audio_debug("set_play_rate(play_rate="<<play_rate<<")");
// Set the play_rate:
_play_rate = play_rate;
if (_sequence != 0) {
play_rate *= _manager->get_play_rate();
S32 percent = (S32)(play_rate * 100.0f);
AIL_set_sequence_tempo(_sequence, percent, 0);
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSequence::length
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
float MilesAudioSequence::
length() const {
S32 length_ms;
AIL_sequence_ms_position(_sequence, &length_ms, NULL);
float time = (float)length_ms * 0.001f;
return time;
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSequence::status
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
AudioSound::SoundStatus MilesAudioSequence::
status() const {
if (_sequence == 0) {
return AudioSound::READY;
}
switch (AIL_sequence_status(_sequence)) {
case SEQ_DONE:
case SEQ_STOPPED:
case SEQ_FREE:
return AudioSound::READY;
case SEQ_PLAYING:
case SEQ_PLAYINGBUTRELEASED:
return AudioSound::PLAYING;
default:
return AudioSound::BAD;
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSequence::cleanup
// Access: Public, Virtual
// Description: Stops the sound from playing and releases any
// associated resources, in preparation for releasing
// the sound or shutting down the sound system.
////////////////////////////////////////////////////////////////////
void MilesAudioSequence::
cleanup() {
stop();
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSequence::internal_stop
// Access: Private
// Description: Called by the GlobalMilesManager when it is detected
// that this particular sound has already stopped, and
// its sequence handle will be recycled.
////////////////////////////////////////////////////////////////////
void MilesAudioSequence::
internal_stop() {
_sequence = 0;
_sequence_index = 0;
}
#endif //]

View File

@ -0,0 +1,91 @@
// Filename: milesAudioSequence.h
// Created by: drose (31Jul07)
//
////////////////////////////////////////////////////////////////////
//
// 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 MILESAUDIOSEQUENCE_H
#define MILESAUDIOSEQUENCE_H
#include "pandabase.h"
#ifdef HAVE_RAD_MSS //[
#include "milesAudioSound.h"
#include "milesAudioManager.h"
#include "mss.h"
////////////////////////////////////////////////////////////////////
// Class : MilesAudioSequence
// Description : A MIDI file, preloaded and played from a memory
// buffer. MIDI files cannot be streamed.
////////////////////////////////////////////////////////////////////
class EXPCL_MILES_AUDIO MilesAudioSequence : public MilesAudioSound {
private:
MilesAudioSequence(MilesAudioManager *manager,
MilesAudioManager::SoundData *sd,
const string &file_name);
public:
virtual ~MilesAudioSequence();
virtual void play();
virtual void stop();
virtual void set_time(float time=0.0f);
virtual float get_time() const;
virtual void set_volume(float volume=1.0f);
virtual void set_balance(float balance_right=0.0f);
virtual void set_play_rate(float play_rate=1.0f);
virtual float length() const;
virtual AudioSound::SoundStatus status() const;
virtual void cleanup();
private:
void internal_stop();
PT(MilesAudioManager::SoundData) _sd;
HSEQUENCE _sequence;
size_t _sequence_index;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
MilesAudioSound::init_type();
register_type(_type_handle, "MilesAudioSequence",
MilesAudioSound::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;
friend class GlobalMilesManager;
friend class MilesAudioManager;
};
#include "milesAudioSequence.I"
#endif //]
#endif /* MILESAUDIOSEQUENCE_H */

View File

@ -1,6 +1,5 @@
// Filename: milesAudioSound.I
// Created by: skyler (June 6, 2001)
// Prior system by: cary
// Created by: drose (30Jul07)
//
////////////////////////////////////////////////////////////////////
//
@ -17,8 +16,3 @@
//
////////////////////////////////////////////////////////////////////

View File

@ -1,5 +1,5 @@
// Filename: milesAudioSound.cxx
// Created by: skyler (June 6, 2001)
// Created by: drose (30Jul07)
//
////////////////////////////////////////////////////////////////////
//
@ -16,308 +16,67 @@
//
////////////////////////////////////////////////////////////////////
#include "pandabase.h"
#include "milesAudioSound.h"
#ifdef HAVE_RAD_MSS //[
#include "throw_event.h"
#include "milesAudioSound.h"
#include "milesAudioManager.h"
TypeHandle MilesAudioSound::_type_handle;
#define NEED_MILES_LENGTH_WORKAROUND
#if (((MSS_MAJOR_VERSION == 6) && (MSS_MINOR_VERSION >= 5)) || (MSS_MAJOR_VERSION >= 7))
#define MILES_6_5_OR_HIGHER
#endif
#undef miles_audio_debug
#ifndef NDEBUG //[
namespace {
////////////////////////////////////////////////////////////////////
// Function: get_status_char
// Access:
// Description:
////////////////////////////////////////////////////////////////////
char
get_status_char(HAUDIO audio) {
if (!audio) {
return '0'; // NULL.
}
switch (AIL_quick_status(audio)) {
case QSTAT_LOADED:
case QSTAT_DONE:
return 'r'; // Ready.
case QSTAT_PLAYING:
return 'p'; // Playing.
default:
return 'x'; // bad.
}
}
#define miles_audio_debug(x) \
audio_debug("MilesAudioSound "<<get_status_char(_audio)<<" \""<<get_name() \
<<"\" "<< x )
}
audio_debug("MilesAudioSound \""<<get_name()<<"\" "<< x )
#else //][
#define miles_audio_debug(x) ((void)0)
#endif //]
namespace {
AILSEQUENCECB sequence_callback = 0;
AILSAMPLECB sample_callback = 0;
const int user_data_index = 7;
////////////////////////////////////////////////////////////////////
// Function: pandaAudioAilCallback_Sequence
// Access: file scope
// Description: This function is part of a hack for finish callbacks
// when using the Miles quick API.
////////////////////////////////////////////////////////////////////
void AILCALLBACK
pandaAudioAilCallback_Sequence(HSEQUENCE S) {
assert(S);
AutoAilLock milesLock;
audio_debug("pandaAudioAilCallback_Sequence(HSEQUENCE="<<((void*)S)<<")");
MilesAudioSound* sound = (MilesAudioSound*)AIL_sequence_user_data(
S, user_data_index);
assert(sound);
sound->finished();
if (sequence_callback) {
sequence_callback(S);
}
}
////////////////////////////////////////////////////////////////////
// Function: pandaAudioAilCallback_Sample
// Access: file scope
// Description: This function is part of a hack for finish callbacks
// when using the Miles quick API.
////////////////////////////////////////////////////////////////////
void AILCALLBACK
pandaAudioAilCallback_Sample(HSAMPLE S) {
assert(S);
AutoAilLock milesLock;
audio_debug("pandaAudioAilCallback_Sample(HSAMPLE="<<((void*)S)<<")");
MilesAudioSound* sound = (MilesAudioSound*)AIL_sample_user_data(
S, user_data_index);
assert(sound);
sound->finished();
if (sample_callback) {
sample_callback(S);
}
}
////////////////////////////////////////////////////////////////////
// Function: panda_AIL_quick_set_finished_callback
// Access: file scope
// Description: This function is part of a hack for finish callbacks
// when using the Miles quick API.
//
// This will determine whether the sound is a MIDI or
// a wave sample and setup the correct callback.
////////////////////////////////////////////////////////////////////
void
panda_AIL_quick_set_finished_callback(HAUDIO audio, MilesAudioSound* sound) {
audio_debug("panda_AIL_quick_set_finished_callback(audio="<<((void*)audio)
<<", sound="<<((void*)sound)<<")");
if (!audio || !sound) {
return;
}
AutoAilLock milesLock;
if (audio->handle != NULL) {
switch (audio->type) {
case AIL_QUICK_XMIDI_TYPE:
audio_debug(" AIL_register_sequence_callback");
AIL_set_sequence_user_data(
(HSEQUENCE)audio->handle, user_data_index, (long)sound);
sequence_callback=AIL_register_sequence_callback(
(HSEQUENCE)audio->handle, pandaAudioAilCallback_Sequence);
audio_debug( "AILCALLBACK "<<((void*)sequence_callback));
break;
case AIL_QUICK_DIGITAL_TYPE:
case AIL_QUICK_MPEG_DIGITAL_TYPE:
audio_debug(" AIL_register_EOS_callback");
AIL_set_sample_user_data(
(HSAMPLE)audio->handle, user_data_index, (long)sound);
sample_callback=AIL_register_EOS_callback(
(HSAMPLE)audio->handle, pandaAudioAilCallback_Sample);
audio_debug(" AILCALLBACK "<<((void*)sample_callback));
break;
default:
audio_debug(" unknown audio type");
break;
}
}
}
S32
Get_playback_rate(HAUDIO audio) {
if (audio) {
switch (audio->type) {
case AIL_QUICK_XMIDI_TYPE:
case AIL_QUICK_DLS_XMIDI_TYPE:
return AIL_sequence_tempo((HSEQUENCE)audio->handle);
break;
case AIL_QUICK_DIGITAL_TYPE:
case AIL_QUICK_MPEG_DIGITAL_TYPE:
if (AIL_quick_status(audio) == QSTAT_PLAYING) {
return AIL_sample_playback_rate((HSAMPLE)audio->handle);
} else {
//HACK: The play rate is returning 0 unless it's playing
if (AIL_quick_play(audio, 1)) {
S32 rate = AIL_sample_playback_rate((HSAMPLE)audio->handle);
AIL_quick_halt(audio);
return rate;
}
}
break;
default:
audio_debug("Get_playback_rate unknown audio type");
break;
}
}
return 0;
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::MilesAudioSound
// Access:
// Function: MilesAudioSound::Constructor
// Access: Private
// Description:
////////////////////////////////////////////////////////////////////
MilesAudioSound::
MilesAudioSound(MilesAudioManager *manager,
MilesAudioManager::SoundData *sd, string file_name, float length)
: _sd(sd), _manager(manager), _file_name(file_name),
_volume(1.0f), _balance(0),
_loop_count(1), _length(length),
_active(true), _paused(false) {
nassertv(sd != NULL);
const string &file_name) :
_manager(manager),
_file_name(file_name),
_volume(1.0f), _balance(0), _play_rate(1.0f),
_loop_count(1),
_active(true), _paused(false)
{
nassertv(!file_name.empty());
audio_debug("MilesAudioSound(manager=0x"<<(void*)&manager
<<", sd=0x"<<(void*)sd<<", file_name="<<file_name<<")");
// Make our own copy of the sound header data:
_audio=AIL_quick_copy(sd->_audio);
_play_rate = 1.0f;
_original_playback_rate = Get_playback_rate(_audio);
audio_debug("MilesAudioSound: original_playback_rate " << _original_playback_rate);
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::~MilesAudioSound
// Access:
// Description:
////////////////////////////////////////////////////////////////////
MilesAudioSound::
~MilesAudioSound() {
miles_audio_debug("~MilesAudioSound()");
cleanup();
_manager->release_sound(this);
miles_audio_debug("~MilesAudioSound() done");
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::play
// Access:
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioSound::
play() {
#if 0
if(_file_name.find(".mid")!=string::npos) {
miles_audio_debug("play() midi");
}
#endif
miles_audio_debug("play()");
if (_active) {
if (status() == AudioSound::PLAYING) {
stop();
}
nassertv(_audio);
_manager->starting_sound(this);
// Start playing:
if (AIL_quick_play(_audio, _loop_count)) {
//#*#panda_AIL_quick_set_finished_callback(_audio, this);
// assert(status()==PLAYING);
audio_debug(" started sound " << _file_name );
} else {
audio_debug(" sound " << _file_name<<" failed to start, err: " <<AIL_last_error());
}
} else {
// In case _loop_count gets set to forever (zero):
audio_debug(" paused "<<_file_name );
_paused=true;
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::stop
// Access:
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioSound::
stop() {
miles_audio_debug("stop()");
_manager->stopping_sound(this);
// The _paused flag should not be cleared here. _paused is not like
// the Pause button on a cd/dvd player. It is used as a flag to say
// that it was looping when it was set inactive. There is no need to
// make this symmetrical with play(). set_active() is the 'owner' of
// _paused. play() accesses _paused to help in the situation where
// someone calls play on an inactive sound().
nassertv(_audio);
AIL_quick_halt(_audio);
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::finished
// Access:
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioSound::
finished() {
miles_audio_debug("finished()");
_manager->stopping_sound(this);
if (!_finished_event.empty()) {
throw_event(_finished_event);
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::set_loop
// Access:
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioSound::
set_loop(bool loop) {
miles_audio_debug("set_loop(loop="<<loop<<")");
// loop count of 0 means always loop
set_loop_count((loop)?0:1);
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::get_loop
// Access:
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
bool MilesAudioSound::
get_loop() const {
miles_audio_debug("get_loop() returning "<<(_loop_count==0));
return (_loop_count == 0);
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::
// Access:
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioSound::
set_loop_count(unsigned long loop_count) {
miles_audio_debug("set_loop_count(loop_count="<<loop_count<<")");
if (_loop_count != loop_count) {
_loop_count = loop_count;
if (status() == PLAYING) {
@ -328,7 +87,6 @@ set_loop_count(unsigned long loop_count) {
// need to stop and start the sound, feel free. Or, maybe I'll spend
// time on it in the future. Please set the loop option before starting
// the sound.
stop();
play();
}
}
@ -336,200 +94,47 @@ set_loop_count(unsigned long loop_count) {
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::
// Access:
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
unsigned long MilesAudioSound::
get_loop_count() const {
miles_audio_debug("get_loop_count() returning "<<_loop_count);
return _loop_count;
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::
// Access:
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioSound::
set_time(float time) {
miles_audio_debug("set_time(time="<<time<<")");
nassertv(_audio);
// Ensure we don't inadvertently run off the end of the sound.
float max_time = length();
if (time > max_time) {
milesAudio_cat.warning()
<< "set_time(" << time << ") requested for sound of length "
<< max_time << "\n";
time = max_time;
}
S32 millisecond_time=(S32)(1000*time);
AIL_quick_set_ms_position(_audio, millisecond_time);
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::
// Access:
// Description:
////////////////////////////////////////////////////////////////////
float MilesAudioSound::
get_time() const {
nassertr(_audio, 0.0f);
S32 millisecond_time=AIL_quick_ms_position(_audio);
float time=float(millisecond_time*.001);
miles_audio_debug("get_time() returning "<<time);
return time;
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::
// Access:
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioSound::
set_volume(float volume) {
miles_audio_debug("set_volume(volume="<<volume<<")");
nassertv(_audio);
// *Set the volume even if our volume is not changing, because the
// MilesAudioManager will call set_volume when *its* volume changes.
// Set the volume:
_volume=volume;
// Account for the category of sound:
volume*=_manager->get_volume();
#ifdef MILES_6_5_OR_HIGHER
// Change to Miles volume, range 0 to 1.0:
F32 milesVolume=volume;
milesVolume=min(milesVolume,1.0f);
milesVolume=max(milesVolume,0.0f);
#else
// Change to Miles volume, range 0 to 127:
S32 milesVolume=((S32)(127*volume))%128;
#endif
// Account for type:
S32 audioType=AIL_quick_type(_audio);
if ((audioType==AIL_QUICK_XMIDI_TYPE) || (audioType==AIL_QUICK_DLS_XMIDI_TYPE)) {
// ...it's a midi file.
// 0 delay, set to this volume immediately
#ifdef MILES_6_5_OR_HIGHER
F32 midiVolDelay =0.0f;
#else
S32 midiVolDelay =0;
#endif
AIL_quick_set_volume(_audio, milesVolume, midiVolDelay);
audio_debug(" volume for this midi is now "<<milesVolume);
} else {
// ...it's a wav or mp3.
#ifdef MILES_6_5_OR_HIGHER
// Convert balance of -1.0..1.0 to 0-1.0:
F32 milesBalance=(F32)((_balance+1.0f)*0.5f);
#else
// Convert balance of -1.0..1.0 to 0..127:
S32 milesBalance=((S32)(63.5f*(_balance+1.0f)))%128;
#endif
AIL_quick_set_volume(_audio, milesVolume, milesBalance);
audio_debug(" volume for this wav or mp3 is now " << milesVolume
<<", balance="<<milesBalance);
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::get_volume
// Access:
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
float MilesAudioSound::
get_volume() const {
miles_audio_debug("get_volume() returning "<<_volume);
return _volume;
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::
// Access:
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioSound::
set_play_rate(float play_rate) {
miles_audio_debug("set_play_rate(play_rate="<<play_rate<<")");
// Set the play_rate:
_play_rate=play_rate;
if (_audio) {
// Account for the category of sound:
play_rate*=_manager->get_play_rate();
switch (_audio->type) {
case AIL_QUICK_XMIDI_TYPE:
case AIL_QUICK_DLS_XMIDI_TYPE:
// midi uses whole percentage values 100 == 100%
_audio->speed = (S32)(play_rate*100.0f);
if ((_audio->speed != -1) && (AIL_quick_status(_audio) == QSTAT_PLAYING)) {
AIL_set_sequence_tempo((HSEQUENCE)_audio->handle, _audio->speed, 0);
}
audio_debug(" play_rate for this midi is now "<<_audio->speed);
break;
case AIL_QUICK_DIGITAL_TYPE:
case AIL_QUICK_MPEG_DIGITAL_TYPE:
// wave and mp3 use sample rate (e.g. 44100)
_audio->speed = (S32)(play_rate*float(_original_playback_rate));
if ((_audio->speed != -1) && (AIL_quick_status(_audio) == QSTAT_PLAYING)) {
AIL_set_sample_playback_rate((HSAMPLE)_audio->handle, _audio->speed);
}
audio_debug(" play_rate for this wav or mp3 is now "<<_audio->speed);
break;
}
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::get_play_rate
// Access:
// Description:
////////////////////////////////////////////////////////////////////
float MilesAudioSound::
get_play_rate() const {
miles_audio_debug("get_play_rate() returning "<<_play_rate);
return _play_rate;
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::set_balance
// Access:
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioSound::
set_balance(float balance_right) {
miles_audio_debug("set_balance(balance_right="<<balance_right<<")");
_balance=balance_right;
// Call set_volume to effect the change:
set_volume(_volume);
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::get_balance
// Access:
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
float MilesAudioSound::
get_balance() const {
audio_debug("MilesAudioSound::get_balance() returning "<<_balance);
return _balance;
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::length
// Access:
// Function: MilesAudioSound::get_play_rate
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
float MilesAudioSound::
length() const {
return _sd->get_length();
get_play_rate() const {
return _play_rate;
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::set_active
// Access:
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioSound::
@ -539,13 +144,12 @@ set_active(bool active) {
_active = active;
if (_active) {
// ...activate the sound.
if (_paused
&&
_loop_count==0) {
if (_paused && _loop_count==0) {
// ...this sound was looping when it was paused.
_paused = false;
play();
}
} else {
// ...deactivate the sound.
if (status() == PLAYING) {
@ -561,85 +165,53 @@ set_active(bool active) {
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::get_active
// Access:
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
bool MilesAudioSound::
get_active() const {
miles_audio_debug("get_active() returning "<<_active);
return _active;
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::set_finished_event
// Access:
// Description:
// Access: Public, Virtual
// Description: This is no longer implemented.
////////////////////////////////////////////////////////////////////
void MilesAudioSound::
set_finished_event(const string &event) {
miles_audio_debug("set_finished_event(event="<<event<<")");
_finished_event = event;
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::get_finished_event
// Access:
// Description:
// Access: Public, Virtual
// Description: This is no longer implemented.
////////////////////////////////////////////////////////////////////
const string &MilesAudioSound::
get_finished_event() const {
miles_audio_debug("get_finished_event() returning "<<_finished_event);
return _finished_event;
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::get_name
// Access:
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
const string &MilesAudioSound::
get_name() const {
//audio_debug("MilesAudioSound::get_name() returning "<<_file_name);
return _file_name;
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::status
// Access:
// Description:
////////////////////////////////////////////////////////////////////
AudioSound::SoundStatus MilesAudioSound::
status() const {
if (!_audio) {
return AudioSound::BAD;
}
switch (AIL_quick_status(_audio)) {
case QSTAT_LOADED:
case QSTAT_DONE:
return AudioSound::READY;
case QSTAT_PLAYING:
return AudioSound::PLAYING;
default:
return AudioSound::BAD;
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioSound::cleanup
// Access: Private
// Description: Called to release any resources associated with the
// sound.
// Access: Public, Virtual
// Description: Stops the sound from playing and releases any
// associated resources, in preparation for releasing
// the sound or shutting down the sound system.
////////////////////////////////////////////////////////////////////
void MilesAudioSound::
cleanup() {
if (_audio) {
stop();
if (MilesAudioManager::_miles_active) {
AIL_quick_unload(_audio);
}
_audio = 0;
}
}
#endif //]

View File

@ -1,6 +1,5 @@
// Filename: milesAudioSound.h
// Created by: skyler (June 6, 2001)
// Prior system by: cary
// Created by: drose (30Jul07)
//
////////////////////////////////////////////////////////////////////
//
@ -17,8 +16,8 @@
//
////////////////////////////////////////////////////////////////////
#ifndef __MILES_AUDIO_SOUND_H__
#define __MILES_AUDIO_SOUND_H__
#ifndef MILESAUDIOSOUND_H
#define MILESAUDIOSOUND_H
#include "pandabase.h"
#ifdef HAVE_RAD_MSS //[
@ -27,116 +26,44 @@
#include "milesAudioManager.h"
#include "mss.h"
class AutoAilLock {
// This will lock and unlock the Miles AIL timer based
// on the current code block. (Auto in the class name
// is referring to "auto variable").
public:
AutoAilLock() {
AIL_lock();
}
~AutoAilLock() {
AIL_unlock();
}
};
////////////////////////////////////////////////////////////////////
// Class : MilesAudioSound
// Description : The base class for both MilesAudioStream and
// MilesAudioSample.
////////////////////////////////////////////////////////////////////
class EXPCL_MILES_AUDIO MilesAudioSound : public AudioSound {
protected:
MilesAudioSound(MilesAudioManager *manager, const string &file_name);
public:
~MilesAudioSound();
virtual void set_loop(bool loop=true);
virtual bool get_loop() const;
// For best compatability, set the loop_count,
// volume, and balance, prior to calling play(). You may
// set them while they're playing, but it's implementation
// specific whether you get the results.
// - Calling play() a second time on the same sound before it is
// finished will start the sound again (creating a skipping or
// stuttering effect).
void play();
void stop();
virtual void set_loop_count(unsigned long loop_count=1);
virtual unsigned long get_loop_count() const;
// loop: false = play once; true = play forever.
// inits to false.
void set_loop(bool loop=true);
bool get_loop() const;
virtual float get_volume() const;
virtual float get_balance() const;
virtual float get_play_rate() const;
// loop_count: 0 = forever; 1 = play once; n = play n times.
// inits to 1.
void set_loop_count(unsigned long loop_count=1);
unsigned long get_loop_count() const;
virtual void set_active(bool active=true);
virtual bool get_active() const;
// Control time position within the sound.
// This is similar (in concept) to the seek position within
// a file.
// time in seconds: 0 = beginning; length() = end.
// inits to 0.0.
// - Unlike the other get_* and set_* calls for a sound, the
// current time position will change while the sound is playing.
// To play the same sound from a time offset a second time,
// explicitly set the time position again. When looping, the
// second and later loops will start from the beginning of the
// sound.
// - If a sound is playing, calling get_time() repeatedly will
// return different results over time. e.g.:
// float percent_complete = s.get_time() / s.length();
void set_time(float time=0.0f);
float get_time() const;
virtual void set_finished_event(const string &event);
virtual const string &get_finished_event() const;
// 0 = minimum; 1.0 = maximum.
// inits to 1.0.
void set_volume(float volume=1.0f);
float get_volume() const;
virtual const string &get_name() const;
// -1.0 is hard left
// 0.0 is centered
// 1.0 is hard right
// inits to 0.0.
void set_balance(float balance_right=0.0f);
float get_balance() const;
virtual void cleanup();
// play_rate is any positive float value.
// inits to 1.0.
void set_play_rate(float play_rate=1.0f);
float get_play_rate() const;
// inits to manager's state.
void set_active(bool active=true);
bool get_active() const;
// This is the string that throw_event() will throw
// when the sound finishes playing. It is not triggered
// when the sound is stopped with stop().
void set_finished_event(const string& event);
const string& get_finished_event() const;
const string& get_name() const;
// return: playing time in seconds.
float length() const;
void set_3d_attributes(float* pos, float* vel);
void get_3d_attributes(float* pos, float* vel);
AudioSound::SoundStatus status() const;
void finished();
private:
PT(MilesAudioManager::SoundData) _sd;
HAUDIO _audio;
protected:
PT(MilesAudioManager) _manager;
string _file_name;
float _volume; // 0..1.0
float _balance; // -1..1
float _play_rate; // 0..1.0
mutable float _length; // in seconds.
unsigned long _loop_count;
S32 _original_playback_rate;
// This is the string that throw_event() will throw
// when the sound finishes playing. It is not triggered
// when the sound is stopped with stop().
string _finished_event;
string _file_name;
// _active is for things like a 'turn off sound effects' in
// a preferences pannel.
@ -149,12 +76,10 @@ private:
// it was set inactive.
bool _paused;
MilesAudioSound(MilesAudioManager* manager, MilesAudioManager::SoundData *sd,
string file_name, float length=0.0f);
void cleanup();
friend class MilesAudioManager;
// This is the string that throw_event() will throw when the sound
// finishes playing. It is not triggered when the sound is stopped
// with stop(). Note: no longer implemented.
string _finished_event;
public:
static TypeHandle get_class_type() {
@ -172,10 +97,12 @@ public:
private:
static TypeHandle _type_handle;
friend class MilesAudioManager;
};
#include "milesAudioSound.I"
#endif //]
#endif /* __MILES_AUDIO_SOUND_H__ */
#endif

View File

@ -0,0 +1,18 @@
// Filename: milesAudioStream.I
// Created by: drose (26Jul07)
//
////////////////////////////////////////////////////////////////////
//
// 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 .
//
////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,299 @@
// Filename: milesAudioStream.cxx
// Created by: drose (26Jul07)
//
////////////////////////////////////////////////////////////////////
//
// 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 .
//
////////////////////////////////////////////////////////////////////
#include "milesAudioStream.h"
#ifdef HAVE_RAD_MSS //[
#include "milesAudioManager.h"
#include "pnotify.h"
TypeHandle MilesAudioStream::_type_handle;
#undef miles_audio_debug
#ifndef NDEBUG //[
#define miles_audio_debug(x) \
audio_debug("MilesAudioStream \""<<get_name()<<"\" "<< x )
#else //][
#define miles_audio_debug(x) ((void)0)
#endif //]
////////////////////////////////////////////////////////////////////
// Function: MilesAudioStream::Constructor
// Access: Private
// Description:
////////////////////////////////////////////////////////////////////
MilesAudioStream::
MilesAudioStream(MilesAudioManager *manager, const string &file_name,
const Filename &path) :
MilesAudioSound(manager, file_name),
_path(path)
{
_stream = 0;
_got_length = false;
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioStream::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
MilesAudioStream::
~MilesAudioStream() {
miles_audio_debug("~MilesAudioStream()");
cleanup();
_manager->release_sound(this);
miles_audio_debug("~MilesAudioStream() done");
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioStream::play
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioStream::
play() {
miles_audio_debug("play()");
if (_active) {
_manager->starting_sound(this);
if (_stream == 0) {
GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
_stream = AIL_open_stream(mgr->_digital_driver, _path.c_str(), 0);
} else {
// We already had the stream open. Keep it open; just restart
// it.
AIL_pause_stream(_stream, 1);
_manager->stop_service_stream(_stream);
}
if (_stream == 0) {
milesAudio_cat.warning()
<< "Could not play " << _file_name << ": too many open streams\n";
} else {
// Start playing:
HSAMPLE sample = AIL_stream_sample_handle(_stream);
nassertv(sample != 0);
_original_playback_rate = AIL_sample_playback_rate(sample);
set_volume(_volume);
set_play_rate(_play_rate);
AIL_set_stream_loop_count(_stream, _loop_count);
if (miles_audio_panda_threads) {
AIL_auto_service_stream(_stream, 0);
_manager->start_service_stream(_stream);
} else {
AIL_auto_service_stream(_stream, 1);
}
AIL_start_stream(_stream);
}
} else {
// In case _loop_count gets set to forever (zero):
audio_debug(" paused "<<_file_name );
_paused = true;
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioStream::stop
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioStream::
stop() {
miles_audio_debug("stop()");
_manager->stopping_sound(this);
// The _paused flag should not be cleared here. _paused is not like
// the Pause button on a cd/dvd player. It is used as a flag to say
// that it was looping when it was set inactive. There is no need to
// make this symmetrical with play(). set_active() is the 'owner' of
// _paused. play() accesses _paused to help in the situation where
// someone calls play on an inactive sound().
if (_stream != 0) {
_manager->stop_service_stream(_stream);
AIL_pause_stream(_stream, 1);
AIL_close_stream(_stream);
_stream = 0;
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioStream::
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioStream::
set_time(float time) {
if (_stream != 0) {
S32 time_ms = (S32)(1000.0f * time);
// Ensure we don't inadvertently run off the end of the sound.
S32 length_ms;
AIL_stream_ms_position(_stream, &length_ms, NULL);
time_ms = min(time_ms, length_ms);
AIL_set_stream_ms_position(_stream, time_ms);
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioStream::
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
float MilesAudioStream::
get_time() const {
nassertr(_stream, 0.0f);
S32 current_ms;
AIL_stream_ms_position(_stream, NULL, &current_ms);
float time = float(current_ms * 0.001f);
return time;
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioStream::
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioStream::
set_volume(float volume) {
_volume = volume;
if (_stream != 0) {
HSAMPLE sample = AIL_stream_sample_handle(_stream);
nassertv(sample != 0);
volume *= _manager->get_volume();
// Change to Miles volume, range 0 to 1.0:
F32 milesVolume = volume;
milesVolume = min(milesVolume, 1.0f);
milesVolume = max(milesVolume, 0.0f);
// Convert balance of -1.0..1.0 to 0-1.0:
F32 milesBalance = (F32)((_balance + 1.0f) * 0.5f);
AIL_set_sample_volume_pan(sample, milesVolume, milesBalance);
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioStream::set_balance
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioStream::
set_balance(float balance_right) {
_balance = balance_right;
// Call set_volume to effect the change:
set_volume(_volume);
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioStream::set_play_rate
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void MilesAudioStream::
set_play_rate(float play_rate) {
_play_rate = play_rate;
if (_stream != 0) {
HSAMPLE sample = AIL_stream_sample_handle(_stream);
nassertv(sample != 0);
play_rate *= _manager->get_play_rate();
// wave and mp3 use sample rate (e.g. 44100)
S32 speed = (S32)(play_rate * (float)_original_playback_rate);
AIL_set_sample_playback_rate(sample, speed);
audio_debug(" play_rate for this wav or mp3 is now " << speed);
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioStream::length
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
float MilesAudioStream::
length() const {
if (!_got_length) {
if (_stream == 0) {
GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
((MilesAudioStream *)this)->_stream = AIL_open_stream(mgr->_digital_driver, _path.c_str(), 0);
}
S32 length_ms;
AIL_stream_ms_position(_stream, &length_ms, NULL);
_length = (float)length_ms * 0.001f;
_got_length = true;
}
return _length;
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioStream::status
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
AudioSound::SoundStatus MilesAudioStream::
status() const {
if (!_stream) {
return AudioSound::READY;
}
switch (AIL_stream_status(_stream)) {
case SMP_STOPPED:
case SMP_DONE:
return AudioSound::READY;
case SMP_PLAYING:
case SMP_PLAYINGBUTRELEASED:
return AudioSound::PLAYING;
default:
return AudioSound::BAD;
}
}
////////////////////////////////////////////////////////////////////
// Function: MilesAudioStream::cleanup
// Access: Public, Virtual
// Description: Called to release any resources associated with the
// sound.
////////////////////////////////////////////////////////////////////
void MilesAudioStream::
cleanup() {
if (_stream) {
stop();
}
}
#endif //]

View File

@ -0,0 +1,91 @@
// Filename: milesAudioStream.h
// Created by: drose (26Jul07)
//
////////////////////////////////////////////////////////////////////
//
// 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 MILESAUDIOSTREAM_H
#define MILESAUDIOSTREAM_H
#include "pandabase.h"
#ifdef HAVE_RAD_MSS //[
#include "milesAudioSound.h"
#include "milesAudioManager.h"
#include "mss.h"
////////////////////////////////////////////////////////////////////
// Class : MilesAudioStream
// Description : This represents a sound file played by the Miles
// Sound System, similar to MilesAudioSample, except
// that it is streamed from disk instead of preloaded
// into memory.
////////////////////////////////////////////////////////////////////
class EXPCL_MILES_AUDIO MilesAudioStream : public MilesAudioSound {
private:
MilesAudioStream(MilesAudioManager *manager, const string &file_name,
const Filename &path);
public:
virtual ~MilesAudioStream();
virtual void play();
virtual void stop();
virtual void set_time(float time=0.0f);
virtual float get_time() const;
virtual void set_volume(float volume=1.0f);
virtual void set_balance(float balance_right=0.0f);
virtual void set_play_rate(float play_rate=1.0f);
virtual float length() const;
virtual AudioSound::SoundStatus status() const;
virtual void cleanup();
private:
Filename _path;
HSTREAM _stream;
S32 _original_playback_rate;
mutable float _length;
mutable bool _got_length;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
MilesAudioSound::init_type();
register_type(_type_handle, "MilesAudioStream",
MilesAudioSound::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;
friend class MilesAudioManager;
};
#include "milesAudioStream.I"
#endif //]
#endif

View File

@ -1,5 +1,9 @@
#include "config_milesAudio.cxx"
#include "milesAudioManager.cxx"
#include "milesAudioSample.cxx"
#include "milesAudioSequence.cxx"
#include "milesAudioSound.cxx"
#include "milesAudioStream.cxx"
#include "globalMilesManager.cxx"