+++ /dev/null
-/* === 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
-
-#include "general.h"
-
-#endif
-
-/* === U S I N G =========================================================== */
-
-using namespace std;
-using namespace etl;
-using namespace synfig;
-
-/* === M A C R O S ========================================================= */
-#ifdef __WIN32
-#else //linux...
-#define AUDIO_OUTPUT FSOUND_OUTPUT_OSS
-#endif
-
-/* === G L O B A L S ======================================================= */
-const double delay_factor = 3;
-
-/* === P R O C E D U R E S ================================================= */
-
-/* === M E T H O D S ======================================================= */
-
-/* === E N T R Y P O I N T ================================================= */
-
-//Help constructing stuff
-struct FSOUND_SAMPLE;
-using studio::AudioContainer;
-
-#ifdef WITH_FMOD
-bool build_profile(FSOUND_SAMPLE *sample, double &samplerate, std::vector<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 std::string &filename,const std::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 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
-}