diff --git a/panda/src/audio/Sources.pp b/panda/src/audio/Sources.pp index 148ce6ec93..3c08acdb6f 100644 --- a/panda/src/audio/Sources.pp +++ b/panda/src/audio/Sources.pp @@ -45,18 +45,23 @@ #end lib_target -// #begin lib_target -// #define TARGET audio_load_st -// #define LOCAL_LIBS \ -// audio -// -// #define SOURCES \ -// audio_load_st.cxx -// -// #end lib_target +#begin lib_target + #define TARGET audio_load_st + #define USE_SOXST yes + #define LOCAL_LIBS \ + audio + + #define SOURCES \ + audio_load_st.cxx + +#end lib_target #begin test_bin_target #define TARGET test_audio + #define LOCAL_LIBS \ + audio + #define OTHER_LIBS \ + $[OTHER_LIBS] pystub #define SOURCES \ test_audio.cxx diff --git a/panda/src/audio/audio_linux_traits.cxx b/panda/src/audio/audio_linux_traits.cxx index 4c0e13ed8c..72e0517ef3 100644 --- a/panda/src/audio/audio_linux_traits.cxx +++ b/panda/src/audio/audio_linux_traits.cxx @@ -12,6 +12,9 @@ #include #include #include +#include +#include +#include typedef set BufferSet; @@ -21,11 +24,11 @@ static byte* buffer1; static byte* buffer2; static byte* current_buffer; static byte* back_buffer; -static byte* zero_buffer; +byte* zero_buffer; static byte* scratch_buffer; static byte* fetch_buffer; -static int want_buffers, have_buffers; -static bool initializing = false; +static int want_buffers = 0, have_buffers = 0; +static bool initializing = true; static int output_fd; static thread* update_thread; static int sample_size = sizeof(short); @@ -98,8 +101,8 @@ static void mix_in(byte* buf, byte* from, float vol, float pan) { signed short in_right = read_buffer(from, i+1); // figure out panning at some point - in_left *= vol; - in_right *= vol; + in_left = (short int)(in_left * vol); + in_right = (short int)(in_right * vol); // compute mixed values left = sound_clamp(left+in_left); @@ -115,7 +118,7 @@ static void mix_buffer(byte* buf) { memcpy(scratch_buffer, zero_buffer, audio_buffer_size); // do stuff for (BufferSet::iterator i=buffers.begin(); i!=buffers.end(); ++i) - min_in(scratch, (*i)->get_buffer(fetch_buffer), 1., 0.); + mix_in(scratch_buffer, (*i)->get_buffer(fetch_buffer), 1., 0.); BufferSet to_del; for (BufferSet::iterator j=buffers.begin(); j!=buffers.end(); ++j) if ((*j)->is_done()) @@ -149,6 +152,9 @@ static void update_linux(void) { // mix 2 buffers and put them in place. mix_buffer(current_buffer); mix_buffer(back_buffer); + if (initializing) + audio_cat->debug() << "mixed 2 buffers, want is currently at (" + << want_buffers << ")" << endl; initializing = false; break; default: @@ -157,7 +163,7 @@ static void update_linux(void) { } static void internal_update(void*) { - if ((output_fd = open(audio_device, O_WRONLY, 0)) == -1) { + if ((output_fd = open(audio_device->c_str(), O_WRONLY, 0)) == -1) { audio_cat->error() << "could not open '" << audio_device << "'" << endl; return; } @@ -221,7 +227,7 @@ static void initialize(void) { audio_cat->info() << "spawning internal update thread" << endl; update_thread = thread::create(internal_update, (void*)0L, - thread_PRIORITY_NORMAL); + thread::PRIORITY_NORMAL); AudioManager::set_update_func(update_linux); have_initialized = true; diff --git a/panda/src/audio/audio_linux_traits.h b/panda/src/audio/audio_linux_traits.h index a1296ff0a1..a238dd35b2 100644 --- a/panda/src/audio/audio_linux_traits.h +++ b/panda/src/audio/audio_linux_traits.h @@ -11,7 +11,14 @@ #ifndef __AUDIO_LINUX_TRAITS_H__ #define __AUDIO_LINUX_TRAITS_H__ +#include "config_audio.h" + +#ifndef HAVE_DEFINED_BYTE typedef unsigned char byte; +#define HAVE_DEFINED_BYTE +#endif /* HAVE_DEFINED_BYTE */ + +extern byte* zero_buffer; class EXPCL_PANDA Buffer { private: @@ -19,15 +26,21 @@ private: unsigned long _size; unsigned long _pos; bool _done; + + INLINE unsigned long buffer_min(unsigned long a, unsigned long b) { + return (a _size) { + unsigned long copy_size = buffer_min(_size-_pos, audio_buffer_size); + unsigned long residue = audio_buffer_size - copy_size; + memcpy(buf, &_data[_pos], copy_size); + _pos += copy_size; + if (residue != 0) { _pos -= _size; - memcpy(&buf[audio_buffer_size-_pos-1], zero_buffer, _pos); + memcpy(&buf[copy_size], zero_buffer, residue); _done = true; } return buf; @@ -36,7 +49,7 @@ public: return _done; } INLINE unsigned long get_size(void) const { - return _size + return _size; } INLINE void reset(void) { _done = false; @@ -57,7 +70,7 @@ public: static void destroy(AudioTraits::SampleClass*); public: // used by the loader - static LinuxSample* load_raw(byte*, unsigned long) + static LinuxSample* load_raw(byte*, unsigned long); // used by the players INLINE Buffer* get_data(void); }; diff --git a/panda/src/audio/audio_load_st.cxx b/panda/src/audio/audio_load_st.cxx index bc77471cf5..62750726c7 100644 --- a/panda/src/audio/audio_load_st.cxx +++ b/panda/src/audio/audio_load_st.cxx @@ -8,11 +8,16 @@ #include "config_audio.h" #include "audio_trait.h" +#ifdef HAVE_SOXST + +extern "C" { #include #include +} #if (PATCHLEVEL == 16) #define FORMATS formats +#define ENCODEFIELD style #define EFFECTS_TYPE struct effect #define STREAM_TYPE struct soundstream #define GETTYPE gettype @@ -21,10 +26,12 @@ #define SIZES sizes #define ENCODING styles #define COPYFORMAT copyformat -#define GETEFFECT geteffect -#define UPDATEEFFECT(a, b, c, d) +#define UPDATEEFFECT(a, b, c, d) \ + (a)->ininfo.channels = (b)->info.channels; \ + (a)->outinfo.channels = (c)->info.channels; #else /* PATCHLEVEL != 16 */ #define FORMATS st_formats +#define ENCODEFIELD encoding #define EFFECTS_TYPE struct st_effect #define STREAM_TYPE struct st_soundstream #define GETTYPE st_gettype @@ -33,45 +40,64 @@ #define SIZES st_sizes_str #define ENCODING st_encodings_str #define COPYFORMAT st_copyformat -#define GETEFFECT st_geteffect #define UPDATEEFFECT st_updateeffect #endif /* PATCHLEVEL */ +#endif /* HAVE_SOXST */ + Configure(audio_load_st); +#ifdef HAVE_SOXST + // the effects we will be using are to change the rate and number of channels static EFFECTS_TYPE efftab[5]; // left/mono channel effects static EFFECTS_TYPE efftabR[5]; // right channel effects static int neffects; // how many effects are in action -static STREAM_TYPE informat; // holder for the input -static STREAM_TYPE outformat; // holder for fake output; +static STREAM_TYPE iformat; // holder for the input +static STREAM_TYPE oformat; // holder for fake output; INLINE static void init_stream(void) { - informat.info.rate = 0; - informat.info.size = -1; - informat.info.encoding = -1; - informat.info.channels = -1; - informat.comment = (char*)0L; - informat.swap = 0; - informat.filetype = (char*)0L; - informat.fp = stdin; - informat.filename = "input"; + iformat.info.rate = 0; + iformat.info.size = -1; + iformat.info.ENCODEFIELD = -1; + iformat.info.channels = -1; + iformat.comment = (char*)0L; + iformat.swap = 0; + iformat.filetype = (char*)0L; + iformat.fp = stdin; + iformat.filename = "input"; - outformat.info.rate = audio_mix_freq; - outformat.info.size = -1; - outformat.info.encoding = -1; - outformat.info.channels = 2; - outformat.comment = (char*)0L; - outformat.swap = 0; - outformat.filetype = (char*)0L; - outformat.fp = stdout; - outformat.filename = "output"; + oformat.info.rate = audio_mix_freq; + oformat.info.size = -1; + oformat.info.ENCODEFIELD = -1; + oformat.info.channels = 2; + oformat.comment = (char*)0L; + oformat.swap = 0; + oformat.filetype = (char*)0L; + oformat.fp = stdout; + oformat.filename = "output"; } +INLINE static void compat_geteffect(EFFECTS_TYPE* eff, const char* name) { +#if (PATCHLEVEL == 16) + eff->name = (char*)name; + geteffect(eff); +#else /* PATCHLEVEL == 16 */ + st_geteffect(eff, name); +#endif /* PATCHLEVEL == 16 */ +} + +typedef void effOptFunc(EFFECTS_TYPE*, int, char**); +#ifndef HAVE_DEFINED_BYTE +typedef unsigned char byte; +#define HAVE_DEFINED_BYTE +#endif /* HAVE_DEFINED_BYTE */ + INLINE static void check_effects(void) { - bool needchan = (informat.info.rate != audio_mix_freq); - bool needrate = (informat.info.channels != 2); + bool needrate = (iformat.info.rate != audio_mix_freq); + bool needchan = (iformat.info.channels != 2); + effOptFunc* func; // efftab[0] is always the input stream and always exists neffects = 1; @@ -79,11 +105,12 @@ INLINE static void check_effects(void) { // if reducing the number of samples, it is faster to run all effects // after the resample effect if (needrate) { - GETEFFECT(&efftab[neffects], "resample"); + compat_geteffect(&efftab[neffects], "resample"); // setup and give default opts - (*efftab[neffects].h->getopts)(&efftab[neffects],(int)0,(char**)0L); + func = (effOptFunc*)(efftab[neffects].h->getopts); + (*func)(&efftab[neffects],(int)0,(char**)0L); // copy format info to effect table - UPDATEEFFECT(&efftab[neffects], &informat, &outformat, 0); + UPDATEEFFECT(&efftab[neffects], &iformat, &oformat, 0); // rate can't handle multiple channels so be sure and account for that if (efftab[neffects].ininfo.channels > 1) memcpy(&efftabR[neffects], &efftab[neffects], sizeof(EFFECTS_TYPE)); @@ -92,53 +119,169 @@ INLINE static void check_effects(void) { // if we ever have more then 2 channels in an input file, we will need to // deal with that somewhere here if (needchan) { - GETEFFECT(&efftab[neffects], "avg"); + compat_geteffect(&efftab[neffects], "avg"); //setup and give default opts - (*efftab[neffects].h->getopts)(&efftab[neffects],(int)0,(char**)0L); + func = (effOptFunc*)(efftab[neffects].h->getopts); + (*func)(&efftab[neffects],(int)0,(char**)0L); // copy format info to effect table - UPDATEEFFECT(&efftab[neffects], &informat, &outformat, 0); + UPDATEEFFECT(&efftab[neffects], &iformat, &oformat, 0); ++neffects; } } -static byte* read_file(Filename filename) { +typedef void effFlowFunc(EFFECTS_TYPE*, LONG*, LONG*, LONG*, LONG*); + +static LONG ibufl[BUFSIZ/2]; +static LONG ibufr[BUFSIZ/2]; +static LONG obufl[BUFSIZ/2]; +static LONG obufr[BUFSIZ/2]; + +static int flow_effect(int e) { + LONG i, done, idone, odone, idonel, odonel, idoner, odoner; + LONG *ibuf, *obuf; + effFlowFunc* eflow; + + // is there any input data? + if (efftab[e-1].odone == efftab[e-1].olen) + return 0; + if (!efftabR[e].name) { + // no stereo data, or effect can handle stereo data. so run effect + // over the entire buffer + idone = efftab[e-1].olen - efftab[e-1].odone; + odone = BUFSIZ; + eflow = (effFlowFunc*)(efftab[e].h->flow); + (*eflow)(&efftab[e], &efftab[e-1].obuf[efftab[e-1].odone], efftab[e].obuf, + &idone, &odone); + efftab[e-1].odone += idone; + efftab[e].odone = 0; + efftab[e].olen = odone; + done = idone + odone; + } else { + // put stereo data in two seperate buffers and run effect on each of them + idone = efftab[e-1].olen - efftab[e-1].odone; + odone = BUFSIZ; + ibuf = &efftab[e-1].obuf[efftab[e-1].odone]; + for (i=0; iflow); + (*eflow)(&efftab[e], ibufl, obufl, &idonel, &odonel); + // right + idoner = idone/2; // odd-length logic + odoner = odone/2; + eflow = (effFlowFunc*)(efftabR[e].h->flow); + (*eflow)(&efftabR[e], ibufr, obufr, &idoner, &odoner); + obuf = efftab[e].obuf; + // this loop implies that left and right effects will always output + // the same amount of data + for (i=0; ierror() << "Effect took & gave no samples!" << endl; + return 1; +} + +typedef void effDrainFunc(EFFECTS_TYPE*, LONG*, LONG*); + +static int drain_effect(int e) { + LONG i, olen, olenl, olenr; + LONG *obuf; + effDrainFunc* edrain; + + if (!efftabR[e].name) { + efftab[e].olen = BUFSIZ; + edrain = (effDrainFunc*)(efftab[e].h->drain); + (*edrain)(&efftab[e], efftab[e].obuf, &efftab[e].olen); + } else { + olen = BUFSIZ; + // left + olenl = olen / 2; + edrain = (effDrainFunc*)(efftab[e].h->drain); + (*edrain)(&efftab[e], obufl, &olenl); + // right + olenr = olen / 2; + edrain = (effDrainFunc*)(efftab[e].h->drain); + (*edrain)(&efftabR[e], obufr, &olenr); + obuf = efftab[e].obuf; + // this loop implies left and right effect will always output the same + // amount of data + for (i=0; ierror() << "could not open '" << filename << "'" << endl; - return (byte*)0L; + *buf = (byte*)0L; + slen = 0; + return; } - informat.filname = filename.c_str(); - informat.filetype = filename.get_extension(); - informat.comment = filename.c_str(); // for lack of anything better + iformat.filename = (char*)filename.c_str(); + iformat.filetype = (char*)filename.get_extension().c_str(); + iformat.comment = (char*)filename.c_str(); // for lack of anything better // now we start some more real work - GETTYPE(&informat); + GETTYPE(&iformat); // read and write starters can change their formats - if ((*informat.h->startread)(&informat) == COMPAT_EOF) { + srfunc = (formatSReadFunc*)(iformat.h->startread); + if ((*srfunc)(&iformat) == COMPAT_EOF) { audio_cat->error() << "failed to start read" << endl; - return (byte*)0L; + *buf = (byte*)0L; + slen = 0; + return; } - CHECKFORMAT(&informat); + CHECKFORMAT(&iformat); if (audio_cat->is_debug()) - audio_cat->debug() << "Input file '" << informat.filename - << "': sample rate = " << informat.info.rate - << " size = " << SIZES[informat.info.size] - << " encoding = " << ENCODING[informat.info.encoding] - << " " << informat.info.channels - << (informat.info.channels > 1)?"channels":"channel" + audio_cat->debug() << "Input file '" << iformat.filename + << "': sample rate = " << iformat.info.rate + << " size = " << SIZES[iformat.info.size] + << " encoding = " << ENCODING[iformat.info.ENCODEFIELD] + << " " << iformat.info.channels + << ((iformat.info.channels > 1)?"channels":"channel") << endl; if (audio_cat->is_debug()) - audio_cat->debug() << "Input file comment: '" << informat.comment << "'" + audio_cat->debug() << "Input file comment: '" << iformat.comment << "'" << endl; - COPYFORMAT(&informat, &outformat); + COPYFORMAT(&iformat, &oformat); check_effects(); // start all effects for (e=1; estart)(&efftab[e]); - if (efftabR[e].name) - (*efftabR[e].h->start)(&efftabR[e]); + esfunc = (effStartFunc*)(efftab[e].h->start); + (*esfunc)(&efftab[e]); + if (efftabR[e].name) { + esfunc = (effStartFunc*)(efftabR[e].h->start); + (*esfunc)(&efftabR[e]); + } } // reserve output buffers for all effects for (e=0; eread)(&informat, efftab[0].obuf, - (LONG)BUFSIZ); + rfunc = (formatReadFunc*)(iformat.h->read); + efftab[0].olen = (*rfunc)(&iformat, efftab[0].obuf, (LONG)BUFSIZ); efftab[0].odone = 0; // run input data thru effects and get more until olen == 0 while (efftab[0].olen > 0) { @@ -167,7 +310,7 @@ static byte* read_file(Filename filename) { for (LONG i=0; i> 16); - unsigned char *b = &bar; + unsigned char *b = (unsigned char*)&bar; out << *b << *(++b); } efftab[neffects-1].odone = efftab[neffects-1].olen; @@ -181,8 +324,8 @@ static byte* read_file(Filename filename) { } } while (havedata); // read another chunk - efftab[0].olen = (*informat.h->read)(&informat, efftab[0].obuf, - (LONG)BUFSIZ); + rfunc = (formatReadFunc*)(iformat.h->read); + efftab[0].olen = (*rfunc)(&iformat, efftab[0].obuf, (LONG)BUFSIZ); efftab[0].odone = 0; } // drain the effects out first to last. push the residue thru subsequent @@ -195,7 +338,7 @@ static byte* read_file(Filename filename) { for (LONG i=0; i> 16); - unsigned char *b = &bar; + unsigned char *b = (unsigned char*)&bar; out << *b << *(++b); } } @@ -204,23 +347,36 @@ static byte* read_file(Filename filename) { } // stop all effects. In so doing, some may generate more data for (e=1; estop)(&efftab[e]); - if (efftabR[e].name) - (*efftabR[e].h->stop)(&efftabR[e]); + estfunc = (effStopFunc*)(efftab[e].h->stop); + (*estfunc)(&efftab[e]); + if (efftabR[e].name) { + estfunc = (effStopFunc*)(efftabR[e].h->stop); + (*estfunc)(&efftabR[e]); + } } // stop reading the file - if ((*informat.h->stopread)(&informat) == COMPAT_EOF) { + strfunc = (formatStopReadFunc*)(iformat.h->stopread); + if ((*strfunc)(&iformat) == COMPAT_EOF) { audio_cat->error() << "error stoping input file" << endl; } - fclose(informat.fp); + fclose(iformat.fp); // generate output string s = out.str(); - int slen = s.length(); - byte* ret = new byte[slen]; - memcpy(ret, s.data(), slen); - return ret; + slen = s.length(); + *buf = new byte[slen]; + memcpy(*buf, s.data(), slen); } +extern "C" { +void cleanup(void) { + // make sure everything is shut down + if (iformat.fp) + fclose(iformat.fp); +} +} + +#endif /* HAVE_SOXST */ + #ifdef AUDIO_USE_MIKMOD void AudioDestroySt(AudioTraits::SampleClass* sample) { @@ -259,18 +415,23 @@ void AudioLoadSt(AudioTraits::SampleClass** sample, #ifdef AUDIO_USE_LINUX +#include "audio_linux_traits.h" + void AudioDestroySt(AudioTraits::SampleClass* sample) { delete sample; } void AudioLoadSt(AudioTraits::SampleClass** sample, AudioTraits::PlayerClass** player, - AudioTraits::DeleteSampleFunc** destroy, Filename) { - audio_cat->warning() << "linux doesn't support reading raw data yet" - << endl; - *sample = (AudioTraits::SampleClass*)0L; - *player = (AudioTraits::PlayerClass*)0L; - *destroy = AudioDestroySt; + AudioTraits::DeleteSampleFunc** destroy, Filename filename) { + byte* buf; + unsigned long len; + read_file(filename, &buf, len); + if (buf != (byte*)0L) { + *sample = LinuxSample::load_raw(buf, len); + *player = LinuxPlayer::get_instance(); + *destroy = AudioDestroySt; + } } #else /* AUDIO_USE_LINUX */ @@ -302,6 +463,7 @@ void AudioLoadSt(AudioTraits::SampleClass** sample, #endif /* AUDIO_USE_MIKMOD */ ConfigureFn(audio_load_st) { +#ifdef HAVE_SOXST for (int i=0; FORMATS[i].names != (char**)0L; ++i) for (int j=0; FORMATS[i].names[j] != (char*)0L; ++j) { if (audio_cat->is_debug()) @@ -309,4 +471,5 @@ ConfigureFn(audio_load_st) { << "'" << endl; AudioPool::register_sample_loader(FORMATS[i].names[j], AudioLoadSt); } +#endif /* HAVE_SOXST */ } diff --git a/panda/src/audio/audio_load_wav.cxx b/panda/src/audio/audio_load_wav.cxx index dacb708138..a6fb2cc208 100644 --- a/panda/src/audio/audio_load_wav.cxx +++ b/panda/src/audio/audio_load_wav.cxx @@ -60,7 +60,7 @@ void AudioLoadWav(AudioTraits::SampleClass** sample, AudioTraits::PlayerClass** player, AudioTraits::DeleteSampleFunc** destroy, Filename) { *sample = (AudioTraits::SampleClass*)0L; - *player = (audioTraits::PlayerClass*)0L; + *player = (AudioTraits::PlayerClass*)0L; *destroy = AudioDestroyWav; } diff --git a/panda/src/audio/audio_trait.h b/panda/src/audio/audio_trait.h index 1915598c6b..c90c3f035d 100644 --- a/panda/src/audio/audio_trait.h +++ b/panda/src/audio/audio_trait.h @@ -55,15 +55,11 @@ public: #ifdef PENV_WIN32 #define AUDIO_USE_WIN32 #else /* PENV_WIN32 */ -/* #ifdef PENV_LINUX #define AUDIO_USE_LINUX -#else /* PENV_LINUX * -*/ +#else /* PENV_LINUX */ #define AUDIO_USE_NULL -/* -#endif /* PENV_LINUX * -*/ +#endif /* PENV_LINUX */ #endif /* PENV_WIN32 */ #endif /* HAVE_MIKMOD */