Release synfigstudio_0_61_07_rc3
[synfig.git] / synfig-studio / tags / synfigstudio_0_61_07_rc3 / src / gtkmm / audiocontainer.cpp
diff --git a/synfig-studio/tags/synfigstudio_0_61_07_rc3/src/gtkmm/audiocontainer.cpp b/synfig-studio/tags/synfigstudio_0_61_07_rc3/src/gtkmm/audiocontainer.cpp
new file mode 100644 (file)
index 0000000..5640ac4
--- /dev/null
@@ -0,0 +1,1427 @@
+/* === 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 <config.h>
+#endif
+
+#include <algorithm>
+#include <sigc++/signal.h>
+
+#include <ETL/stringf>
+#include <ETL/clock>
+//#include <ETL/thread>
+#include <glibmm/thread.h>
+
+#include <synfig/general.h>
+
+#include <glibmm/main.h>
+
+#include "audiocontainer.h"
+
+#include <cstdio>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include <set>
+#include <vector>
+
+#ifdef WITH_FMOD
+#include <fmod.h>
+#endif
+
+#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<char> &samples)
+#else
+bool build_profile(FSOUND_SAMPLE */*sample*/, double &/*samplerate*/, std::vector<char> &/*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<char> 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<scrubelement>       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<scrubinfo**>        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<scrubinfo**>::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<AudioContainer> studio::AudioProfile::get_parent() const
+{
+       return parent;
+}
+
+void studio::AudioProfile::set_parent(etl::handle<AudioContainer> 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 string &filename,const string &filedirectory)
+{
+       if(!imp)
+       {
+               imp = new AudioImp;
+       }
+
+       profilevalid = false;
+       return imp->load(filename,filedirectory);
+}
+
+#ifdef WITH_FMOD
+handle<studio::AudioProfile> studio::AudioContainer::get_profile(float samplerate)
+#else
+handle<studio::AudioProfile> 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<studio::AudioProfile>();
+       }
+
+       //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<studio::AudioProfile>();
+       }
+
+       #else
+
+       return handle<studio::AudioProfile>();
+
+       #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 AudioContainer::AudioImp::start_scrubbing(double t)
+#else
+void 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 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 AudioContainer::AudioImp::scrub(double t)
+#else
+void 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 tweek 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
+}