/* === 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; //Warning: Unused variable delay_factor /* === 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), curscrubpos(), 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(NULL), profilevalid() { } 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 }