+/* === 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
+}