X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=synfig-studio%2Ftags%2Fsynfigstudio_%40VERSION_MAJ%40_%40VERSION_MIN%40_%40VERSION_REV%40%2Fsrc%2Fgtkmm%2Faudiocontainer.cpp;fp=synfig-studio%2Ftags%2Fsynfigstudio_%40VERSION_MAJ%40_%40VERSION_MIN%40_%40VERSION_REV%40%2Fsrc%2Fgtkmm%2Faudiocontainer.cpp;h=70d5dd98e1ea2f88b35f51b60386b1eb3be2aac0;hb=d98a5bdd45e2c836aa7996f22516008f1ce29947;hp=0000000000000000000000000000000000000000;hpb=c33037508a9f5a0607b14f3feed692a0d364d68a;p=synfig.git diff --git a/synfig-studio/tags/synfigstudio_@VERSION_MAJ@_@VERSION_MIN@_@VERSION_REV@/src/gtkmm/audiocontainer.cpp b/synfig-studio/tags/synfigstudio_@VERSION_MAJ@_@VERSION_MIN@_@VERSION_REV@/src/gtkmm/audiocontainer.cpp new file mode 100644 index 0000000..70d5dd9 --- /dev/null +++ b/synfig-studio/tags/synfigstudio_@VERSION_MAJ@_@VERSION_MIN@_@VERSION_REV@/src/gtkmm/audiocontainer.cpp @@ -0,0 +1,1429 @@ +/* === S Y N F I G ========================================================= */ +/*! \file audiocontainer.cpp +** \brief Audio Container implementation File +** +** $Id$ +** +** \legal +** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley +** +** This package is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License as +** published by the Free Software Foundation; either version 2 of +** the License, or (at your option) any later version. +** +** This package is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** \endlegal +*/ +/* ========================================================================= */ + +/* === H E A D E R S ======================================================= */ + +#ifdef USING_PCH +# include "pch.h" +#else +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include +#include +//#include +#include + +#include + +#include + +#include "audiocontainer.h" + +#include +#include +#include + +#include +#include + +#ifdef WITH_FMOD +#include +#endif + +#include "general.h" + +#endif + +/* === U S I N G =========================================================== */ + +using namespace std; +using namespace etl; +using namespace synfig; + +/* === M A C R O S ========================================================= */ +#ifdef __WIN32 +#else //linux... +#define AUDIO_OUTPUT FSOUND_OUTPUT_OSS +#endif + +/* === G L O B A L S ======================================================= */ +const double delay_factor = 3; + +/* === P R O C E D U R E S ================================================= */ + +/* === M E T H O D S ======================================================= */ + +/* === E N T R Y P O I N T ================================================= */ + +//Help constructing stuff +struct FSOUND_SAMPLE; +using studio::AudioContainer; + +#ifdef WITH_FMOD +bool build_profile(FSOUND_SAMPLE *sample, double &samplerate, std::vector &samples) +#else +bool build_profile(FSOUND_SAMPLE */*sample*/, double &/*samplerate*/, std::vector &/*samples*/) +#endif +{ +#ifdef WITH_FMOD + + float sps = samplerate; + + //trivial rejection... + if(!sample || sps < 1) + { + synfig::warning("build_profile: Sample rate was too low or sample was invalid"); + return false; + } + + //lock for all samples and process them into a subset + unsigned int mode = FSOUND_Sample_GetMode(sample); + + //make sure that it's 8 bit... I hope this works... + + //sample rate of the actual song... + int allsamplerate = 0; + FSOUND_Sample_GetDefaults(sample,&allsamplerate,0,0,0); + + //get the size of the sample defaults from the mode + int channels = 1; + int channelsize = 1; //number of bytes + + if(mode & FSOUND_16BITS) channelsize = 2; //this shouldn't happen + if(mode & FSOUND_STEREO) channels = 2; + + //Get the sample information + int samplesize = channels*channelsize; //the only two things that increase samplesize + int numsamples = FSOUND_Sample_GetLength(sample); //number of samples in the sound + int sizeall = samplesize*numsamples; //should be the size of the entire song... + + if(sizeall <= 0) + { + synfig::warning("ProfileAudio: Sample buffer cannot be size smaller than 1 (%X)",FSOUND_GetError()); + return false; + } + + //be sure that the new sample rate is less than or equal to the original + if(sps > allsamplerate) sps = allsamplerate; + + float stride = allsamplerate/(float)sps; + + //down sampling to 8 bit min/max values + synfig::warning("About to downsample from %d Hz to %.1f Hz, sample stride: %f", allsamplerate, sps, stride); + + char *sampledata=0,*useless = 0; + unsigned int len1,len2; + // vector samples; + { + if(!FSOUND_Sample_Lock(sample,0,sizeall,(void**)&sampledata,(void**)&useless,&len1,&len2)) + { + synfig::warning("ProfileAudio: Unable to lock the sound buffer... (%X)",FSOUND_GetError()); + return false; + } + synfig::warning("Locked: %X: %d bytes, %X: %d bytes",sampledata,len1,useless,len2); + + if(channelsize == 1) + { + //process the data + char *iter = sampledata; + char *end = iter + sizeall; + + float curaccum = 0; + float numinc = sps/(float)allsamplerate; + + /* Loop per sample DDA alg. + */ + + int i = 0; + + //HACK - to prevent if statement inside inner loop + //synfig::warning("wo baby wo baby, inc: %d, stride: %f, size: %d", inc, stride, sizeall); + while(iter < end) + { + int maxs = 0, mins = 0; + + for(;curaccum < 1; curaccum += numinc) + { + for(i = 0; iter < end && i < channels; ++i, iter += channelsize) + { + maxs = std::max(maxs,(int)*iter); + mins = std::min(mins,(int)*iter); + } + } + //insert onto new list + samples.push_back(maxs); + samples.push_back(mins); + + //and flush all the used samples for curaccum + curaccum -= 1; + } + }else if(channelsize == 2) + { + //process the data + char *iter = sampledata; + char *end = iter + sizeall; + + float curaccum = 0; + float numinc = sps/(float)allsamplerate; + + /* Loop per sample DDA alg. + */ + + int i = 0; + + //HACK - to prevent if statement inside inner loop + //synfig::warning("wo baby wo baby, inc: %d, stride: %f, size: %d", inc, stride, sizeall); + while(iter < end) + { + int maxs = 0, mins = 0; + + for(;curaccum < 1; curaccum += numinc) + { + for(i = 0; iter < end && i < channels; ++i, iter += channelsize) + { + maxs = std::max(maxs,(int)*(short*)iter); + mins = std::min(mins,(int)*(short*)iter); + } + } + //insert onto new list + samples.push_back(maxs / 256); + samples.push_back(mins / 256); + + //and flush all the used samples for curaccum + curaccum -= 1; + } + } + } + + synfig::warning("Stats: %f seconds with %d bytes now %d bytes", (samples.size()/2)/sps, sizeall, samples.size()); + synfig::warning(" %f seconds before", numsamples/(float)allsamplerate); + + //we're done yay!, unlock + FSOUND_Sample_Unlock(sample,sampledata,useless,len1,len2); + synfig::info("Unlocked"); + + //FSOUND_PlaySound(FSOUND_FREE,sound); //test + + //we're done + samplerate = sps*2; //it must be x2 because we are sampling max and min + + return true; + + #else + + return false; + + #endif +} + + +//FMOD Systemwide Specific data mostly here... + +struct scrubinfo; + +#ifdef WITH_FMOD +static double buffer_length_sec = 0; + +//------- Scrubbing -------------- +/* Scrubbing works as follows: + + The sound is played using PlaySoundEx + we specify a user created DSP for scrubbing + set it initially to inactive + + When the program initiates it + we set the initial data in the shared structure and activate the dsp unit + then for each cursor update we get we set the value in the shared structure +*/ + +/* Things to check: + If IsPlaying just governs the channel play/stop value or if it also concerns the pause state + +*/ + +//so we can know where to create all this stuff +struct scrubinfo +{ + /* Linearly fit the frequency to hit the desired zero point... + */ + /*struct scrubelement + { + double pos; + double dt; + //the amount of time left til the cursor hits this one + // it's incremental so that the cursor must pass previous + // ones before decrementing this value + }; + */ + + //the time it should take to get to the next position... + + //to prevent from writing to the same location at once... (pos, deltatime, delaystart) + //Glib::Mutex lock; + + //the queue system would provide a more accurate representation... + volatile double pos; + volatile double deltatime; + + volatile double delaystart; //the amount of time we need to go before we start interpolating... + + volatile int channel; + + /*std::list queue; + + volatile int channel; + + //current position is FSOUND_GetCurrentPosition and current time is always 0... + + void add(const scrubelement &elem) + { + lock.LockWrite(); + + queue.push_back(elem); + + lock.UnlockWrite(); + } + + //Function to safely get rid of all the old samples (dt < 0) + void flush() + { + lock.LockWrite(); + + while(queue.size() && queue.front().dt < 0) + { + queue.pop_front(); + } + + lock.UnlockWrite(); + }*/ + + void Lock() + { + //lock.lock(); + } + + void Unlock() + { + //lock.unlock(); + } + + //All parameters and state should be set by the time we get here... + void scrub_dsp_process() + { + const double epsilon = 1e-5; + + //Trivial reject... we go nowhere if we aren't playing (hit boundary...) + if(!FSOUND_IsPlaying(channel)) return; + + //Get rid of all the old samples + //flush(); + + //Trivial reject #2 - We also go nowhere with no future samples (pause) + /*if(queue.size() <= 0) + { + FSOUND_SetPaused(channel,true); + return; + }*/ + + double dt = buffer_length_sec; + + //Lock ourselves so we don't die + Lock(); + + //printf("DSP data: delay = %.3f s, pos = %d, dt = %.3f\n", delaystart, (int)pos, deltatime); + + //Check delay + if(delaystart > 0) + { + delaystart -= dt; + + if(delaystart < 0) + { + dt = -delaystart; //add time back... + delaystart = 0; + } + } + + //Trivial reject for if we're past current sample... + if(delaystart > 0 || deltatime <= 0) + { + FSOUND_SetPaused(channel,true); + Unlock(); + return; + } + + //Calculate stretched frequency based on delayed future sample... + + //NOTE: BY NOT TRACKING POSITION AS A FLOAT AND JUST USING THE SOUNDS VALUE + // WE ARE LOSING A TINY AMOUNT OF PRECISION ACCURACY EVERY UPDATE + // (THIS SHOULDN'T BE A PROBLEM) + const double p0 = FSOUND_GetCurrentPosition(channel); + double curdp = 0; + + if(!FSOUND_GetPaused(channel)) + { + curdp = FSOUND_GetFrequency(channel) * deltatime; + } + + //need to rescale derivative... + + //Extrapolate from difference in position and deltatime vs dt... + const double pa = p0 + curdp/2; + + const double p1 = pos; + + //const double pb = p0/3 + p1*2/3; + + //will extrapolate if needed... (could be funky on a curve) + double t = 0; + if(deltatime > epsilon) + { + t = dt / deltatime; + } + + //Decrement deltatime (we may have gone past but that's what happens when we don't get input...) + deltatime -= dt; + + //we don't need to look at the current variables anymore... + Unlock(); + + const double invt = 1-t; + //double deltapos = (p1-p0)*t; //linear version + double deltapos = invt*invt*p0 + 2*t*invt*pa + t*t*p1 - p0; //quadratic smoothing version + + //Attempted cubic smoothing + //const double invt2 = invt*invt; + //const double t2 = t*t; + //double deltapos = invt2*invt*p0 + 3*t*invt2*pa + 3*t2*invt*pb + t2*t*p1; + //double deltapos = p0 + t*(3*(pa-p0) + t*(3*(p0+2*pa+pb) + t*((p1-3*pb+3*ba-p0)))); //unwound cubic + + //printf("\ttime = %.2f; p(%d,%d,%d) dp:%d - delta = %d\n",t,(int)p0,(int)p1,(int)p2,(int)curdp,(int)deltapos); + + //Based on the delta info calculate the stretched frequency + const int dest_samplesize = FSOUND_DSP_GetBufferLength(); + + //rounded to nearest frequency... (hopefully...) + int freq = (int)(deltapos * FSOUND_GetOutputRate() / (double)dest_samplesize); + + //NOTE: WE MIGHT WANT TO DO THIS TO BE MORE ACCURATE BUT YEAH... ISSUES WITH SMALL NUMBERS + //double newdp = deltapos / t; + + //printf("\tfreq = %d Hz\n", freq); + + // !If I failed... um assume we have to pause it... ? + if(abs(freq) < 100) + { + FSOUND_SetPaused(channel,true); + }else + { + //synfig::info("DSP f = %d Hz", freq); + FSOUND_SetPaused(channel,false); + if(!FSOUND_SetFrequency(channel,freq)) + { + //ERROR WILL ROBINSON!!!... + printf("Error in Freq... what do I do?\n"); + } + } + } +}; + +struct scrubuserdata +{ + /* //for use with multiple + //each one is a 'handle' to a pointer that will be effected by something else + typedef scrubinfo** value_type; + typedef std::set< value_type > scrubslist; + scrubslist scrubs; + + //so we can lock access to the list... + ReadWriteLock lock; + + void AddScrub(scrubinfo **i) + { + lock.LockWrite(); + scrubs.insert(i); + lock.UnLockWrite(); + } + + void RemoveScrub(scrubinfo **i) + { + lock.LockWrite(); + scrubs.erase(i); + lock.UnLockWrite(); + }*/ + + scrubinfo * volatile * scrub; +}; + +//Scrubbing data structures +static const int default_scrub_priority = 5; //between clear and sfx/music mix +static scrubuserdata g_scrubdata = {0}; +static FSOUND_DSPUNIT *scrubdspunit = 0; + +void * scrubdspwrap(void *originalbuffer, void *newbuffer, int length, void *userdata) +{ + //std::string dsp = "DSP"; + if(userdata) + { + scrubuserdata &sd = *(scrubuserdata*)userdata; + + /* //For use with multiple scrubs... + //Lock so no one can write to it while we're reading from it... + sd.lock.LockRead(); + + //make a copy of it... + std::vector v(sd.scrubs.begin(),sd.scrubs.end()); + + //other things can do stuff with it again... + sd.lock.UnLockRead(); + + //loop through the list and process all the active scrub units + std::vector::iterator i = v.begin(), + end = v.end(); + for(;i != end; ++i) + { + //check to make sure this object is active... + if(*i && **i) + { + (**i)->scrub_dsp_process(); + } + } + */ + + if(sd.scrub && *sd.scrub) + { + //dsp += " processing..."; + scrubinfo * info = (*sd.scrub); + info->scrub_dsp_process(); + } + } + + //synfig::info(dsp); + + return newbuffer; +} + +//------- Class for loading fmod on demand ------- + +class FMODInitializer +{ + bool loaded; + int refcount; + +public: + FMODInitializer():loaded(false),refcount(0) {} + ~FMODInitializer() + { + clear(); + } + + void addref() + { + if(!loaded) + { + #ifdef WITH_FMOD + synfig::info("Initializing FMOD on demand..."); + + { + FSOUND_SetOutput(AUDIO_OUTPUT); + + /*int numdrivers = FSOUND_GetNumDrivers(); + synfig::info("Num FMOD drivers = %d",numdrivers); + synfig::info("Current Driver is #%d", FSOUND_GetDriver()); + + for(int i = 0; i < numdrivers; ++i) + { + unsigned int caps = 0; + FSOUND_GetDriverCaps(i,&caps); + + synfig::info(" Caps for driver %d (%s) = %x",i,FSOUND_GetDriverName(i),caps); + } + + FSOUND_SetDriver(0);*/ + + //Modify buffer size... + //FSOUND_SetBufferSize(100); + + if(!FSOUND_Init(44100, 32, 0)) + { + synfig::warning("Unable to load FMOD"); + }else + { + loaded = true; + + //Create the DSP for processing scrubbing... + scrubdspunit = FSOUND_DSP_Create(&scrubdspwrap,default_scrub_priority,&g_scrubdata); + + //Load the number of sec per buffer into the global variable... + buffer_length_sec = FSOUND_DSP_GetBufferLength() / (double)FSOUND_GetOutputRate(); + } + } + #endif + } + + //add to the refcount + ++refcount; + //synfig::info("Audio: increment fmod refcount %d", refcount); + } + + void decref() + { + if(refcount <= 0) + { + synfig::warning("FMOD refcount is already 0..."); + }else + { + --refcount; + //synfig::info("Audio: decrement fmod refcount %d", refcount); + + //NOTE: UNCOMMENT THIS IF YOU WANT FMOD TO UNLOAD ITSELF WHEN IT ISN'T NEEDED ANYMORE... + flush(); + } + } + + bool is_loaded() const { return loaded; } + + void clear() + { + refcount = 0; + flush(); + } + + void flush() + { + if(loaded && refcount <= 0) + { + #ifdef WITH_FMOD + synfig::info("Unloading FMOD"); + if(scrubdspunit) FSOUND_DSP_Free(scrubdspunit); + FSOUND_Close(); + #endif + loaded = false; + } + } +}; + +//The global counter for FMOD.... +FMODInitializer fmodinit; + +#endif + +//----- AudioProfile Implementation ----------- +void studio::AudioProfile::clear() +{ + samplerate = 0; + samples.clear(); +} + +handle studio::AudioProfile::get_parent() const +{ + return parent; +} + +void studio::AudioProfile::set_parent(etl::handle i) +{ + parent = i; +} + +double studio::AudioProfile::get_offset() const +{ + if(parent) + return parent->get_offset(); + return 0; +} + +//---------- AudioContainer definitions --------------------- + +struct studio::AudioContainer::AudioImp +{ + //Sample load time information + FSOUND_SAMPLE * sample; + int channel; + int sfreq; + int length; + + //Time information + double offset; //time offset for playing... + + //We don't need it now that we've adopted the play(t) time schedule... + //current time... and playing info.... + //float seekpost; + //bool useseekval; + + //Make sure to sever our delayed start if we are stopped prematurely + sigc::connection delaycon; + + //Action information + bool playing; + double curscrubpos; + etl::clock timer; //for getting the time diff between scrub input points + + //Scrubbing information... + //the current position of the sound will be sufficient for normal stuff... + #ifdef WITH_FMOD + scrubinfo scrinfo; + #endif + + scrubinfo *scrptr; + + bool is_scrubbing() const {return scrptr != 0;} +#ifdef WITH_FMOD + void set_scrubbing(bool s) +#else + void set_scrubbing(bool /*s*/) +#endif + { + #ifdef WITH_FMOD + if(s) + scrptr = &scrinfo; + else + #endif + scrptr = 0; + } + + //helper to make sure we are actually playing (and to get a new channel...) + bool init_play() + { + #ifdef WITH_FMOD + if(!FSOUND_IsPlaying(channel)) + { + if(sample) + { + //play sound paused etc. + channel = FSOUND_PlaySoundEx(FSOUND_FREE,sample,0,true); + if(channel < 0 || FSOUND_GetError() != FMOD_ERR_NONE) + { + synfig::warning("Could not play the sample..."); + return false; + } + } + }else + { + FSOUND_SetPaused(channel,true); + FSOUND_SetFrequency(channel,sfreq); + } + return true; + + #else + + return false; + + #endif + } + +public: //structors + AudioImp() + :sample(0), + channel(0), + sfreq(0), + length(0), + offset(0), + playing(false), + scrptr(0) + { + //reuse the channel... + #ifdef WITH_FMOD + channel = FSOUND_FREE; + #endif + } + + ~AudioImp() + { + clear(); + } + +public: //helper/accessor funcs + bool start_playing_now() //callback for timer... + { + #ifdef WITH_FMOD + if(playing) + { + //Make sure the sound is playing and if it is un pause it... + if(init_play()) + FSOUND_SetPaused(channel,false); + } + #endif + + return false; //so the timer doesn't repeat itself + } + + bool isRunning() + { + #ifdef WITH_FMOD + return FSOUND_IsPlaying(channel); + #else + return false; + #endif + } + + bool isPaused() + { +#ifdef WITH_FMOD + return FSOUND_GetPaused(channel); +#else + return false; +#endif + } + + +public: //forward interface + + //Accessors for the offset - in seconds + const double &get_offset() const {return offset;} + void set_offset(const double &d) + { + offset = d; + } + + //Will override the parameter timevalue if the sound is running, and not if it's not... +#ifdef WITH_FMOD + bool get_current_time(double &out) +#else + bool get_current_time(double &/*out*/) +#endif + { + if(isRunning()) + { + #ifdef WITH_FMOD + unsigned int pos = FSOUND_GetCurrentPosition(channel); + + //adjust back by 1 frame... HACK.... + //pos -= FSOUND_DSP_GetBufferLength(); + + //set the position + out = pos/(double)sfreq + offset; + #endif + + return true; + } + return false; + } + + //Big implementation functions... + bool load(const std::string &filename, const std::string &filedirectory); + void clear(); + + //playing functions + void play(double t); + void stop(); + + //scrubbing functions + void start_scrubbing(double t); + void scrub(double t); + void stop_scrubbing(); + + double scrub_time() + { + return curscrubpos; + } +}; + +//--------------- Audio Container definitions -------------------------- +studio::AudioContainer::AudioContainer() +{ + imp = 0; +} + +studio::AudioContainer::~AudioContainer() +{ + if(imp) delete (imp); +} + +bool studio::AudioContainer::load(const std::string &filename,const std::string &filedirectory) +{ + if(!imp) + { + imp = new AudioImp; + } + + profilevalid = false; + return imp->load(filename,filedirectory); +} + +#ifdef WITH_FMOD +handle studio::AudioContainer::get_profile(float samplerate) +#else +handle studio::AudioContainer::get_profile(float /*samplerate*/) +#endif +{ + #ifdef WITH_FMOD + + //if we already have done our work, then we're good + if(profilevalid && prof) + { + //synfig::info("Using already built profile"); + return prof; + } + + //synfig::info("Before profile"); + //make a new profile at current sample rate + + //NOTE: We might want to reuse the structure already there... + prof = new AudioProfile; + prof->set_parent(this); //Our parent is THIS!!! + + if(!prof) + { + synfig::warning("Couldn't allocate audioprofile..."); + return handle(); + } + + //setting the info for the sample rate + //synfig::info("Setting info..."); + + synfig::info("Building Profile..."); + prof->samplerate = samplerate; + if(build_profile(imp->sample,prof->samplerate,prof->samples)) + { + synfig::info(" Success!"); + profilevalid = true; + return prof; + }else + { + return handle(); + } + + #else + + return handle(); + + #endif +} + +void studio::AudioContainer::clear() +{ + if(imp) + { + delete imp; + imp = 0; + } + + profilevalid = false; +} + +void studio::AudioContainer::play(double t) +{ + if(imp) imp->play(t); +} + +void studio::AudioContainer::stop() +{ + if(imp) imp->stop(); +} + +bool studio::AudioContainer::get_current_time(double &out) +{ + if(imp) return imp->get_current_time(out); + else return false; +} + +void AudioContainer::set_offset(const double &s) +{ + if(imp) imp->set_offset(s); +} + +double AudioContainer::get_offset() const +{ + static double zero = 0; + if(imp) + return imp->get_offset(); + return zero; +} + +bool AudioContainer::is_playing() const +{ + if(imp) + return imp->playing; + return false; +} + +bool AudioContainer::is_scrubbing() const +{ + if(imp) + return imp->is_scrubbing(); + return false; +} + +void AudioContainer::start_scrubbing(double t) +{ + if(imp) imp->start_scrubbing(t); +} + +void AudioContainer::stop_scrubbing() +{ + if(imp) imp->stop_scrubbing(); +} + +void AudioContainer::scrub(double t) +{ + if(imp) imp->scrub(t); +} + +double AudioContainer::scrub_time() const +{ + if(imp) return imp->scrub_time(); + else return 0; +} + +bool AudioContainer::isRunning() const +{ + if(imp) return imp->isRunning(); + else return false; +} + +bool AudioContainer::isPaused() const +{ + if(imp) return imp->isPaused(); + else return false; +} + +//----------- Audio imp information ------------------- + +#ifdef WITH_FMOD +bool studio::AudioContainer::AudioImp::load(const std::string &filename, + const std::string &filedirectory) +#else +bool studio::AudioContainer::AudioImp::load(const std::string &/*filename*/, + const std::string &/*filedirectory*/) +#endif +{ + clear(); + + #ifdef WITH_FMOD + + //And continue with the sound loading... + string file = filename; + + //Trivial reject... (fixes stat call problem... where it just looks at directory and not file...) + if(file.length() == 0) return false; + + //we don't need the file directory? + if(!is_absolute_path(file)) + { + file=filedirectory+filename; + synfig::warning("Not absolute hoooray"); + } + synfig::info("Loading Audio file: %s", file.c_str()); + + //check to see if file exists + { + struct stat s; + if(stat(file.c_str(),&s) == -1 && errno == ENOENT) + { + synfig::info("There was no audio file..."); + return false; + } + } + + //load fmod if we can... + //synfig::warning("I'm compiled with FMOD!"); + fmodinit.addref(); + + //load the stream + int ch = FSOUND_FREE; + FSOUND_SAMPLE *sm = FSOUND_Sample_Load(FSOUND_FREE,file.c_str(),FSOUND_LOOP_OFF|FSOUND_MPEGACCURATE,0,0); + + if(!sm) + { + synfig::warning("Could not open the audio file as a sample: %s",file.c_str()); + goto error; + } + + //synfig::warning("Opened a file as a sample! :)"); + + /*{ + int bufferlen = FSOUND_DSP_GetBufferLength(); + synfig::info("Buffer length = %d samples, %.3lf s",bufferlen, bufferlen / (double)FSOUND_GetOutputRate()); + }*/ + + //set all the variables since everything has worked out... + //get the length of the stream + { + length = FSOUND_Sample_GetLength(sm); + + int volume = 0; + FSOUND_Sample_GetDefaults(sm,&sfreq,&volume,0,0); + + //double len = length / (double)sfreq; + //synfig::info("Sound info: %.2lf s long, %d Hz, %d Vol",(double)length,sfreq,volume); + } + + //synfig::warning("Got all info, and setting up everything, %.2f sec.", length); + //synfig::warning(" BigSample: composed of %d samples", FSOUND_Sample_GetLength(sm)); + synfig::info("Successfully opened %s as a sample and initialized it.",file.c_str()); + + //set up the playable info + sample = sm; + channel = ch; + + //the length and sfreq params have already been initialized + + return true; + +error: + if(sm) FSOUND_Sample_Free(sm); + file = ""; + + fmodinit.decref(); + + return false; + + #else + return false; + #endif +} + +#ifdef WITH_FMOD +void studio::AudioContainer::AudioImp::play(double t) +#else +void studio::AudioContainer::AudioImp::play(double /*t*/) +#endif +{ + #ifdef WITH_FMOD + if(!sample) return; + + //stop scrubbing if we are... + if(is_scrubbing()) stop_scrubbing(); + + //t -= offset; + t -= get_offset(); + playing = true; + + if(t < 0) + { + unsigned int timeout = (int)floor(-t * 1000 + 0.5); + //synfig::info("Playing audio delayed by %d ms",timeout); + //delay for t seconds... + delaycon = Glib::signal_timeout().connect( + sigc::mem_fun(*this,&studio::AudioContainer::AudioImp::start_playing_now),timeout); + + init_play(); + FSOUND_SetFrequency(channel,sfreq); + FSOUND_SetCurrentPosition(channel,0); + return; + } + + unsigned int position = (int)floor(t*sfreq + 0.5); + + if(position >= FSOUND_Sample_GetLength(sample)) + { + synfig::warning("Can't play audio when past length..."); + return; + } + + init_play(); + FSOUND_SetFrequency(channel,sfreq); + FSOUND_SetCurrentPosition(channel,position); + FSOUND_SetPaused(channel,false); + + //synfig::info("Playing audio with position %d samples",position); + + #endif +} + +void studio::AudioContainer::AudioImp::stop() +{ + delaycon.disconnect(); + + #ifdef WITH_FMOD + if(fmodinit.is_loaded() && playing && isRunning()) + { + FSOUND_SetPaused(channel,true); + } + #endif + + playing = false; +} + +void studio::AudioContainer::AudioImp::clear() +{ + #ifdef WITH_FMOD + delaycon.disconnect(); + + stop(); + stop_scrubbing(); + + if(sample) + { + if(FSOUND_IsPlaying(channel)) + { + FSOUND_StopSound(channel); + } + channel = FSOUND_FREE; + FSOUND_Sample_Free(sample); + fmodinit.decref(); + } + + playing = false; + + #else + channel = 0; + #endif + + sample = 0; + playing = false; +} + +#ifdef WITH_FMOD +void studio::AudioContainer::AudioImp::start_scrubbing(double t) +#else +void studio::AudioContainer::AudioImp::start_scrubbing(double /*t*/) +#endif +{ + //synfig::info("Start scrubbing: %lf", t); + if(playing) stop(); + + set_scrubbing(true); + + #ifdef WITH_FMOD + //make sure the other one is not scrubbing... + if(g_scrubdata.scrub) + { + *g_scrubdata.scrub = 0; //nullify the pointer... + } + + //Set up the initial state for the delayed audio position + scrinfo.delaystart = 0; + scrinfo.pos = 0; + scrinfo.deltatime = 0; + + //set it to point to our pointer (dizzy...) + g_scrubdata.scrub = &scrptr; + + //setup position info so we can know what to do on boundary conditions... + curscrubpos = (t - get_offset()) * sfreq; + + //So we can get an accurate difference... + timer.reset(); + + //reposition the sound if it won't be when scrubbed (if it's already in the range...) + int curi = (int)curscrubpos; + if(curi >= 0 && curi < length) + { + init_play(); + FSOUND_SetCurrentPosition(channel,curi); + + //Set the values... + scrinfo.pos = curscrubpos; + scrinfo.delaystart = delay_factor*buffer_length_sec; + + //synfig::info("\tStarting at %d samps, with %d p %.3f delay", + // FSOUND_GetCurrentPosition(channel), (int)scrinfo.pos, scrinfo.delaystart); + } + + + + //enable the dsp... + //synfig::info("\tActivating DSP"); + FSOUND_DSP_SetActive(scrubdspunit,true); + #endif +} + +void studio::AudioContainer::AudioImp::stop_scrubbing() +{ + //synfig::info("Stop scrubbing"); + + if(is_scrubbing()) + { + set_scrubbing(false); + + #ifdef WITH_FMOD + g_scrubdata.scrub = 0; + + //stop the dsp... + //synfig::info("\tDeactivating DSP"); + FSOUND_DSP_SetActive(scrubdspunit,false); + if(FSOUND_IsPlaying(channel)) FSOUND_SetPaused(channel,true); + #endif + } + + curscrubpos = 0; +} + +#ifdef WITH_FMOD +void studio::AudioContainer::AudioImp::scrub(double t) +#else +void studio::AudioContainer::AudioImp::scrub(double /*t*/) +#endif +{ + #ifdef WITH_FMOD + //synfig::info("Scrub to %lf",t); + if(is_scrubbing()) + { + //What should we do? + + /* Different special cases + All outside, all inside, + coming in (left or right), + going out (left or right) + */ + double oldpos = curscrubpos; + double newpos = (t - get_offset()) * sfreq; + + curscrubpos = newpos; + + //Ok the sound is running, now we need to tweak it + if(newpos > oldpos) + { + //Outside so completely stopped... + if(newpos < 0 || oldpos >= length) + { + //synfig::info("\tOut +"); + if(FSOUND_IsPlaying(channel)) + { + FSOUND_SetPaused(channel,true); + } + + //Zero out the data! + scrinfo.Lock(); + scrinfo.delaystart = 0; + scrinfo.deltatime = 0; + scrinfo.Unlock(); + + return; + } + + //going in? - start the sound at the beginning... + /*else if(oldpos < 0) + { + //Set up the sound to be playing paused at the start... + init_play(); + FSOUND_SetCurrentPosition(channel,0); + + synfig::info("\tIn + %d", FSOUND_GetCurrentPosition(channel)); + + scrinfo.Lock(); + scrinfo.pos = 0; + scrinfo.delaystart = delay_factor*buffer_length_sec; + scrinfo.deltatime = 0; + scrinfo.Unlock(); + }*/ + //don't need to deal with leaving... automatically dealt with... + + else //We're all inside... + { + //Set new position and decide what to do with time... + scrinfo.Lock(); + scrinfo.pos = newpos; + + //should we restart the delay cycle... (is it done?) + if(!isRunning() || (scrinfo.delaystart <= 0 && scrinfo.deltatime <= 0 && isPaused())) + { + //synfig::info("Starting + at %d",(int)newpos); + scrinfo.deltatime = 0; + scrinfo.delaystart = delay_factor*buffer_length_sec; + scrinfo.Unlock(); + + //Set up the sound paused at the current position + init_play(); + int setpos = min(max((int)newpos,0),length); + FSOUND_SetCurrentPosition(channel,setpos); + timer.reset(); + return; + } + + //No! just increment the time delta... + scrinfo.deltatime += timer.pop_time(); + + //Nope... continue and just increment the deltatime and reset position... + scrinfo.Unlock(); + + //set channel and unpause + FSOUND_SetPaused(channel,false); + scrinfo.channel = channel; + + } + }else if(newpos < oldpos) + { + //completely stopped... + if(newpos >= length || oldpos < 0) + { + //synfig::info("Out -"); + if(FSOUND_IsPlaying(channel)) + { + FSOUND_SetPaused(channel,true); + } + + //Zero out the data! + scrinfo.Lock(); + scrinfo.delaystart = 0; + scrinfo.deltatime = 0; + scrinfo.Unlock(); + } + + //going in? - start going backwards at the end... + /*else if(oldpos >= length) + { + synfig::info("In -"); + //Set up the sound to be playing paused at the start... + init_play(); + FSOUND_SetCurrentPosition(channel,length-1); + + scrinfo.Lock(); + scrinfo.pos = length-1; + scrinfo.delaystart = delay_factor*buffer_length_sec; + scrinfo.deltatime = 0; + scrinfo.Unlock(); + }*/ + //we don't have to worry about the leaving case... + + else //We're all inside... + { + //Set new position and decide what to do with time... + scrinfo.Lock(); + scrinfo.pos = newpos; + + //should we restart the delay cycle... (is it done?) + if(!isRunning() ||(scrinfo.delaystart <= 0 && scrinfo.deltatime <= 0 && isPaused())) + { + //synfig::info("Starting - at %d",(int)newpos); + scrinfo.deltatime = 0; + scrinfo.delaystart = delay_factor*buffer_length_sec; + scrinfo.Unlock(); + + //reset timing so next update will be a valid diff... + init_play(); + int setpos = min(max((int)newpos,0),length); + FSOUND_SetCurrentPosition(channel,setpos); + timer.reset(); + return; + } + + //No! just increment the time delta... + scrinfo.deltatime += timer.pop_time(); + + //Nope... continue and just increment the deltatime and reset position... + scrinfo.Unlock(); + + //set channel and unpause + FSOUND_SetPaused(channel,false); + scrinfo.channel = channel; + } + } + } + #endif +}