Added "--with-userdir" to configure script to change the default user settings directory
[synfig.git] / synfig-studio / trunk / src / gtkmm / audiocontainer.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file audiocontainer.cpp
3 **      \brief Audio Container implementation File
4 **
5 **      $Id: audiocontainer.cpp,v 1.1.1.1 2005/01/07 03:34:35 darco Exp $
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **
10 **      This package is free software; you can redistribute it and/or
11 **      modify it under the terms of the GNU General Public License as
12 **      published by the Free Software Foundation; either version 2 of
13 **      the License, or (at your option) any later version.
14 **
15 **      This package is distributed in the hope that it will be useful,
16 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
17 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 **      General Public License for more details.
19 **      \endlegal
20 */
21 /* ========================================================================= */
22
23 /* === H E A D E R S ======================================================= */
24
25 #ifdef USING_PCH
26 #       include "pch.h"
27 #else
28 #ifdef HAVE_CONFIG_H
29 #       include <config.h>
30 #endif
31
32 #include <algorithm>
33 #include <sigc++/signal.h>
34
35 #include <ETL/stringf>
36 #include <ETL/clock>
37 //#include <ETL/thread>
38 #include <glibmm/thread.h>
39
40 #include <synfig/general.h>
41
42 #include <glibmm/main.h>
43
44 #include "audiocontainer.h"
45
46 #include <cstdio>
47 #include <sys/stat.h>
48 #include <errno.h>
49
50 #include <set>
51 #include <vector>
52
53 #ifdef WITH_FMOD
54 #include <fmod.h>
55 #endif
56
57 #endif
58
59 /* === U S I N G =========================================================== */
60
61 using namespace std;
62 using namespace etl;
63 using namespace synfig;
64
65 /* === M A C R O S ========================================================= */
66 #ifdef __WIN32
67 #else //linux...
68 #define AUDIO_OUTPUT    FSOUND_OUTPUT_OSS
69 #endif
70
71 /* === G L O B A L S ======================================================= */
72 const double delay_factor = 3;
73
74 /* === P R O C E D U R E S ================================================= */
75
76 /* === M E T H O D S ======================================================= */
77
78 /* === E N T R Y P O I N T ================================================= */
79
80 //Help constructing stuff
81 struct FSOUND_SAMPLE;
82 using studio::AudioContainer;
83
84 bool build_profile(FSOUND_SAMPLE *sample, double &samplerate, std::vector<char> &samples)
85 {
86 #ifdef WITH_FMOD
87         
88         float sps = samplerate;
89         
90         //trivial rejection...
91         if(!sample || sps < 1)
92         {
93                 synfig::warning("build_profile: Sample rate was too low or sample was invalid");
94                 return false;
95         }
96         
97         //lock for all samples and process them into a subset
98         unsigned int mode = FSOUND_Sample_GetMode(sample);
99         
100         //make sure that it's 8 bit... I hope this works...
101         
102         //sample rate of the actual song...
103         int allsamplerate = 0;
104         FSOUND_Sample_GetDefaults(sample,&allsamplerate,0,0,0);
105         
106         //get the size of the sample defaults from the mode
107         int channels = 1;
108         int channelsize = 1; //number of bytes
109         
110         if(mode & FSOUND_16BITS) channelsize = 2; //this shouldn't happen
111         if(mode & FSOUND_STEREO) channels = 2;
112                         
113         //Get the sample information
114         int samplesize = channels*channelsize; //the only two things that increase samplesize
115         int numsamples = FSOUND_Sample_GetLength(sample); //number of samples in the sound
116         int sizeall = samplesize*numsamples; //should be the size of the entire song...
117         
118         if(sizeall <= 0)
119         {
120                 synfig::warning("ProfileAudio: Sample buffer cannot be size smaller than 1 (%X)",FSOUND_GetError());
121                 return false;
122         }
123         
124         //be sure that the new sample rate is less than or equal to the original
125         if(sps > allsamplerate) sps = allsamplerate;
126                 
127         float stride = allsamplerate/(float)sps;
128         
129         //down sampling to 8 bit min/max values 
130         synfig::warning("About to downsample from %d Hz to %.1f Hz, sample stride: %f", allsamplerate, sps, stride);
131         
132         char *sampledata=0,*useless = 0;
133         unsigned int len1,len2;
134         // vector<char> samples;
135         {
136                 if(!FSOUND_Sample_Lock(sample,0,sizeall,(void**)&sampledata,(void**)&useless,&len1,&len2))
137                 {
138                         synfig::warning("ProfileAudio: Unable to lock the sound buffer... (%X)",FSOUND_GetError());
139                         return false;
140                 }
141                 synfig::warning("Locked: %X: %d bytes, %X: %d bytes",sampledata,len1,useless,len2);
142                 
143                 if(channelsize == 1)
144                 {
145                         //process the data
146                         char *iter = sampledata;
147                         char *end = iter + sizeall;
148                         
149                         float curaccum = 0;
150                         float numinc = sps/(float)allsamplerate;
151                         
152                         /* Loop per sample DDA alg.
153                         */
154                         
155                         int i = 0;
156                         
157                         //HACK - to prevent if statement inside inner loop
158                         //synfig::warning("wo baby wo baby, inc: %d, stride: %f, size: %d", inc, stride, sizeall);
159                         while(iter < end)
160                         {
161                                 int maxs = 0, mins = 0;
162                                 
163                                 for(;curaccum < 1; curaccum += numinc)
164                                 {
165                                         for(i = 0; iter < end && i < channels; ++i, iter += channelsize)
166                                         {
167                                                 maxs = std::max(maxs,(int)*iter);
168                                                 mins = std::min(mins,(int)*iter);
169                                         }
170                                 }
171                                 //insert onto new list
172                                 samples.push_back(maxs);
173                                 samples.push_back(mins);
174                                                 
175                                 //and flush all the used samples for curaccum
176                                 curaccum -= 1;
177                         }
178                 }else if(channelsize == 2)
179                 {
180                         //process the data
181                         char *iter = sampledata;
182                         char *end = iter + sizeall;
183                         
184                         float curaccum = 0;
185                         float numinc = sps/(float)allsamplerate;
186                         
187                         /* Loop per sample DDA alg.
188                         */
189                         
190                         int i = 0;
191                         
192                         //HACK - to prevent if statement inside inner loop
193                         //synfig::warning("wo baby wo baby, inc: %d, stride: %f, size: %d", inc, stride, sizeall);
194                         while(iter < end)
195                         {
196                                 int maxs = 0, mins = 0;
197                                 
198                                 for(;curaccum < 1; curaccum += numinc)
199                                 {
200                                         for(i = 0; iter < end && i < channels; ++i, iter += channelsize)
201                                         {
202                                                 maxs = std::max(maxs,(int)*(short*)iter);
203                                                 mins = std::min(mins,(int)*(short*)iter);
204                                         }
205                                 }
206                                 //insert onto new list
207                                 samples.push_back(maxs / 256);
208                                 samples.push_back(mins / 256);
209                                                 
210                                 //and flush all the used samples for curaccum
211                                 curaccum -= 1;
212                         }
213                 }
214         }
215         
216         synfig::warning("Stats: %f seconds with %d bytes now %d bytes", (samples.size()/2)/sps, sizeall, samples.size());
217         synfig::warning("               %f seconds before", numsamples/(float)allsamplerate);
218         
219         //we're done yay!, unlock
220         FSOUND_Sample_Unlock(sample,sampledata,useless,len1,len2);
221         synfig::info("Unlocked");
222         
223         //FSOUND_PlaySound(FSOUND_FREE,sound); //test
224         
225         //we're done
226         samplerate = sps*2; //it must be x2 because we are sampling max and min
227         
228         return true;
229         
230         #else
231         
232         return false;
233         
234         #endif
235 }
236
237
238 //FMOD Systemwide Specific data mostly here...
239
240 struct scrubinfo;
241         
242 #ifdef WITH_FMOD
243 static double   buffer_length_sec = 0;
244
245 //------- Scrubbing --------------
246 /* Scrubbing works as follows:
247
248         The sound is played using PlaySoundEx
249                 we specify a user created DSP for scrubbing
250                 set it initially to inactive
251
252         When the program initiates it
253                 we set the initial data in the shared structure and activate the dsp unit
254                 then for each cursor update we get we set the value in the shared structure
255 */
256
257 /* Things to check:
258         If IsPlaying just governs the channel play/stop value or if it also concerns the pause state
259         
260 */
261
262 //so we can know where to create all this stuff
263 struct scrubinfo
264 {
265         /*      Linearly fit the frequency to hit the desired zero point...
266         */      
267         /*struct scrubelement
268         {
269                 double  pos;
270                 double  dt;
271                 //the amount of time left til the cursor hits this one
272                 //      it's incremental so that the cursor must pass previous 
273                 //      ones before decrementing this value
274         };      
275         */
276         
277         //the time it should take to get to the next position...
278         
279         //to prevent from writing to the same location at once... (pos, deltatime, delaystart)
280         //Glib::Mutex   lock;
281         
282         //the queue system would provide a more accurate representation...
283         volatile double pos;
284         volatile double deltatime;
285         
286         volatile double delaystart; //the amount of time we need to go before we start interpolating...
287         
288         volatile int    channel;
289
290         /*std::list<scrubelement>       queue;
291         
292         volatile int    channel;
293         
294         //current position is FSOUND_GetCurrentPosition and current time is always 0...
295         
296         void add(const scrubelement &elem)
297         {
298                 lock.LockWrite();
299                 
300                 queue.push_back(elem);
301                 
302                 lock.UnlockWrite();
303         }
304         
305         //Function to safely get rid of all the old samples (dt < 0)
306         void flush()
307         {
308                 lock.LockWrite();
309                 
310                 while(queue.size() && queue.front().dt < 0)
311                 {
312                         queue.pop_front();
313                 }
314                 
315                 lock.UnlockWrite();             
316         }*/
317         
318         void Lock()
319         {
320                 //lock.lock();
321         }
322         
323         void Unlock()
324         {
325                 //lock.unlock();
326         }
327         
328         //All parameters and state should be set by the time we get here...
329         void scrub_dsp_process()
330         {
331                 const double epsilon = 1e-5;
332                 
333                 //Trivial reject... we go nowhere if we aren't playing (hit boundary...)
334                 if(!FSOUND_IsPlaying(channel)) return;
335                         
336                 //Get rid of all the old samples
337                 //flush();
338                 
339                 //Trivial reject #2 - We also go nowhere with no future samples (pause)
340                 /*if(queue.size() <= 0)
341                 {
342                         FSOUND_SetPaused(channel,true);
343                         return;
344                 }*/
345                 
346                 double dt = buffer_length_sec;
347                 
348                 //Lock ourselves so we don't die
349                 Lock();
350                 
351                 //printf("DSP data: delay = %.3f s, pos = %d, dt = %.3f\n", delaystart, (int)pos, deltatime);
352                 
353                 //Check delay
354                 if(delaystart > 0)
355                 {
356                         delaystart -= dt;
357                         
358                         if(delaystart < 0)
359                         {
360                                 dt = -delaystart; //add time back...
361                                 delaystart = 0;
362                         }
363                 }
364                 
365                 //Trivial reject for if we're past current sample...
366                 if(delaystart > 0 || deltatime <= 0)
367                 {
368                         FSOUND_SetPaused(channel,true);
369                         Unlock();
370                         return;
371                 }
372                         
373                 //Calculate stretched frequency based on delayed future sample...
374                 
375                 //NOTE: BY NOT TRACKING POSITION AS A FLOAT AND JUST USING THE SOUNDS VALUE
376                 //              WE ARE LOSING A TINY AMOUNT OF PRECISION ACCURACY EVERY UPDATE 
377                 //              (THIS SHOULDN'T BE A PROBLEM)
378                 const double p0 = FSOUND_GetCurrentPosition(channel);           
379                 double curdp = 0;
380                 
381                 if(!FSOUND_GetPaused(channel))
382                 {
383                         curdp = FSOUND_GetFrequency(channel) * deltatime;
384                 }
385
386                 //need to rescale derivative...         
387
388                 //Extrapolate from difference in position and deltatime vs dt...
389                 const double pa = p0 + curdp/2;
390                 
391                 const double p1 = pos;
392                 
393                 //const double pb = p0/3 + p1*2/3;
394                 
395                 //will extrapolate if needed... (could be funky on a curve)
396                 double t = 0;
397                 if(deltatime > epsilon)
398                 {
399                         t = dt / deltatime;
400                 }
401                 
402                 //Decrement deltatime (we may have gone past but that's what happens when we don't get input...)
403                 deltatime -= dt;
404                 
405                 //we don't need to look at the current variables anymore...
406                 Unlock();
407                                 
408                 const double invt = 1-t;
409                 //double deltapos = (p1-p0)*t; //linear version
410                 double deltapos = invt*invt*p0 + 2*t*invt*pa + t*t*p1 - p0; //quadratic smoothing version
411                 
412                 //Attempted cubic smoothing
413                 //const double invt2 = invt*invt;
414                 //const double t2 = t*t;
415                 //double deltapos = invt2*invt*p0 + 3*t*invt2*pa + 3*t2*invt*pb + t2*t*p1;
416                 //double deltapos = p0 + t*(3*(pa-p0) + t*(3*(p0+2*pa+pb) + t*((p1-3*pb+3*ba-p0)))); //unwound cubic
417                                 
418                 //printf("\ttime = %.2f; p(%d,%d,%d) dp:%d - delta = %d\n",t,(int)p0,(int)p1,(int)p2,(int)curdp,(int)deltapos);
419                 
420                 //Based on the delta info calculate the stretched frequency
421                 const int dest_samplesize = FSOUND_DSP_GetBufferLength();
422                 
423                 //rounded to nearest frequency... (hopefully...)
424                 int freq = (int)(deltapos * FSOUND_GetOutputRate() / (double)dest_samplesize);
425                 
426                 //NOTE: WE MIGHT WANT TO DO THIS TO BE MORE ACCURATE BUT YEAH... ISSUES WITH SMALL NUMBERS
427                 //double newdp = deltapos / t;
428
429                 //printf("\tfreq = %d Hz\n", freq);
430                 
431                 // !If I failed... um assume we have to pause it... ?
432                 if(abs(freq) < 100)
433                 {
434                         FSOUND_SetPaused(channel,true);
435                 }else
436                 {
437                         //synfig::info("DSP f = %d Hz", freq);
438                         FSOUND_SetPaused(channel,false);
439                         if(!FSOUND_SetFrequency(channel,freq))
440                         {
441                                 //ERROR WILL ROBINSON!!!...                     
442                                 printf("Error in Freq... what do I do?\n");
443                         }
444                 }
445         }       
446 };
447
448 struct scrubuserdata
449 {
450         /* //for use with multiple
451         //each one is a 'handle' to a pointer that will be effected by something else
452         typedef scrubinfo**     value_type;
453         typedef std::set< value_type > scrubslist;
454         scrubslist              scrubs;
455         
456         //so we can lock access to the list...
457         ReadWriteLock   lock;
458         
459         void AddScrub(scrubinfo **i)
460         {
461                 lock.LockWrite();
462                 scrubs.insert(i);
463                 lock.UnLockWrite();
464         }
465         
466         void RemoveScrub(scrubinfo **i)
467         {
468                 lock.LockWrite();
469                 scrubs.erase(i);
470                 lock.UnLockWrite();             
471         }*/
472         
473         scrubinfo * volatile *  scrub;
474 };
475
476 //Scrubbing data structures
477 static const int                default_scrub_priority = 5; //between clear and sfx/music mix
478 static scrubuserdata    g_scrubdata = {0};
479 static FSOUND_DSPUNIT   *scrubdspunit = 0;
480
481 void * scrubdspwrap(void *originalbuffer, void *newbuffer, int length, void *userdata)
482 {
483         //std::string   dsp = "DSP";
484         if(userdata)
485         {
486                 scrubuserdata &sd = *(scrubuserdata*)userdata;
487                 
488                 /* //For use with multiple scrubs...
489                 //Lock so no one can write to it while we're reading from it...
490                 sd.lock.LockRead();
491                 
492                 //make a copy of it...          
493                 std::vector<scrubinfo**>        v(sd.scrubs.begin(),sd.scrubs.end());
494                 
495                 //other things can do stuff with it again...
496                 sd.lock.UnLockRead();
497                 
498                 //loop through the list and process all the active scrub units          
499                 std::vector<scrubinfo**>::iterator      i = v.begin(),
500                                                                                         end = v.end();          
501                 for(;i != end; ++i)
502                 {
503                         //check to make sure this object is active...
504                         if(*i && **i)
505                         {
506                                 (**i)->scrub_dsp_process();
507                         }
508                 }
509                 */
510                 
511                 if(sd.scrub && *sd.scrub)
512                 {
513                         //dsp += " processing...";
514                         scrubinfo * info = (*sd.scrub);                 
515                         info->scrub_dsp_process();
516                 }
517         }
518         
519         //synfig::info(dsp);
520
521         return newbuffer;
522 }
523
524 //------- Class for loading fmod on demand -------
525
526 class FMODInitializer
527 {
528         bool loaded;
529         int     refcount;
530         
531 public:
532         FMODInitializer():loaded(false),refcount(0) {}
533         ~FMODInitializer() 
534         {
535                 clear();
536         }
537         
538         void addref()
539         {
540                 if(!loaded)
541                 {
542                         #ifdef WITH_FMOD
543                         synfig::info("Initializing FMOD on demand...");
544                         
545                         {
546                                 FSOUND_SetOutput(AUDIO_OUTPUT);
547                                 
548                                 /*int numdrivers = FSOUND_GetNumDrivers();
549                                 synfig::info("Num FMOD drivers = %d",numdrivers);
550                                 synfig::info("Current Driver is #%d", FSOUND_GetDriver());
551                                 
552                                 for(int i = 0; i < numdrivers; ++i)
553                                 {
554                                         unsigned int caps = 0;
555                                         FSOUND_GetDriverCaps(i,&caps);
556                                         
557                                         synfig::info("   Caps for driver %d (%s) = %x",i,FSOUND_GetDriverName(i),caps);
558                                 }
559                                 
560                                 FSOUND_SetDriver(0);*/
561                                 
562                                 //Modify buffer size...
563                                 //FSOUND_SetBufferSize(100);
564                                 
565                                 if(!FSOUND_Init(44100, 32, 0))
566                                 {
567                                         synfig::warning("Unable to load FMOD");
568                                 }else
569                                 {
570                                         loaded = true;
571                                         
572                                         //Create the DSP for processing scrubbing...
573                                         scrubdspunit = FSOUND_DSP_Create(&scrubdspwrap,default_scrub_priority,&g_scrubdata);
574                                         
575                                         //Load the number of sec per buffer into the global variable...
576                                         buffer_length_sec = FSOUND_DSP_GetBufferLength() / (double)FSOUND_GetOutputRate();
577                                 }
578                         }
579                         #endif
580                 }
581                 
582                 //add to the refcount
583                 ++refcount;
584                 //synfig::info("Audio: increment fmod refcount %d", refcount);
585         }
586         
587         void decref()
588         {
589                 if(refcount <= 0)
590                 {
591                         synfig::warning("FMOD refcount is already 0...");
592                 }else
593                 {
594                         --refcount;
595                         //synfig::info("Audio: decrement fmod refcount %d", refcount);
596                         
597                         //NOTE: UNCOMMENT THIS IF YOU WANT FMOD TO UNLOAD ITSELF WHEN IT ISN'T NEEDED ANYMORE...
598                         flush();
599                 }
600         }
601
602         bool is_loaded() const { return loaded; }
603         
604         void clear()
605         {
606                 refcount = 0;
607                 flush();
608         }
609         
610         void flush()
611         {
612                 if(loaded && refcount <= 0)
613                 {
614                         #ifdef WITH_FMOD
615                         synfig::info("Unloading FMOD");
616                         if(scrubdspunit) FSOUND_DSP_Free(scrubdspunit);
617                         FSOUND_Close();                 
618                         #endif
619                         loaded = false;
620                 }
621         }
622 };
623
624 //The global counter for FMOD....
625 FMODInitializer         fmodinit;
626
627 #endif
628
629 //----- AudioProfile Implementation -----------
630 void studio::AudioProfile::clear()
631 {
632         samplerate = 0;
633         samples.clear();
634 }
635
636 handle<AudioContainer>  studio::AudioProfile::get_parent() const
637 {
638         return parent;
639 }
640
641 void studio::AudioProfile::set_parent(etl::handle<AudioContainer> i)
642 {
643         parent = i;
644 }
645
646 double studio::AudioProfile::get_offset() const
647 {
648         if(parent)
649                 return parent->get_offset();
650         return 0;
651 }
652
653 //---------- AudioContainer definitions ---------------------
654
655 struct studio::AudioContainer::AudioImp
656 {       
657         //Sample load time information
658         FSOUND_SAMPLE *         sample;
659         int                                     channel;
660         int                                     sfreq;
661         int                                     length;
662         
663         //Time information
664         double                          offset; //time offset for playing...
665         
666         //We don't need it now that we've adopted the play(t) time schedule...
667         //current time... and playing info.... 
668         //float                         seekpost;
669         //bool                          useseekval;
670         
671         //Make sure to sever our delayed start if we are stopped prematurely
672         sigc::connection        delaycon;
673         
674         //Action information
675         bool                            playing;
676         double                          curscrubpos;
677         etl::clock                      timer;  //for getting the time diff between scrub input points
678                 
679         //Scrubbing information...
680         //the current position of the sound will be sufficient for normal stuff...
681         #ifdef WITH_FMOD
682         scrubinfo                       scrinfo;
683         #endif
684         
685         scrubinfo                       *scrptr;
686         
687         bool is_scrubbing() const {return scrptr != 0;}
688         void set_scrubbing(bool s)
689         {
690                 #ifdef WITH_FMOD
691                 if(s)
692                         scrptr = &scrinfo;
693                 else 
694                 #endif
695                 scrptr = 0;
696         }
697         
698         //helper to make sure we are actually playing (and to get a new channel...)
699         bool init_play()
700         {
701                 #ifdef WITH_FMOD
702                 if(!FSOUND_IsPlaying(channel))
703                 {
704                         if(sample)
705                         {
706                                 //play sound paused etc.
707                                 channel = FSOUND_PlaySoundEx(FSOUND_FREE,sample,0,true);
708                                 if(channel < 0 || FSOUND_GetError() != FMOD_ERR_NONE)
709                                 {
710                                         synfig::warning("Could not play the sample...");
711                                         return false;
712                                 }
713                         }
714                 }else
715                 {
716                         FSOUND_SetPaused(channel,true);
717                         FSOUND_SetFrequency(channel,sfreq);
718                 }
719                 return true;
720                 
721                 #else
722                 
723                 return false;
724                 
725                 #endif
726         }
727         
728 public: //structors
729         AudioImp()
730         :sample(0),
731         channel(0),
732         sfreq(0),
733         length(0),
734         offset(0),
735         playing(false),
736         scrptr(0)       
737         {
738                 //reuse the channel...
739                 #ifdef WITH_FMOD
740                 channel = FSOUND_FREE;
741                 #endif
742         }
743         
744         ~AudioImp()
745         {
746                 clear();
747         }
748         
749 public: //helper/accessor funcs
750         bool start_playing_now() //callback for timer...
751         {
752                 #ifdef WITH_FMOD
753                 if(playing)
754                 {
755                         //Make sure the sound is playing and if it is un pause it...
756                         if(init_play())
757                                 FSOUND_SetPaused(channel,false);
758                 }
759                 #endif
760                 
761                 return false; //so the timer doesn't repeat itself
762         }       
763         
764         bool isRunning()
765         {
766                 #ifdef WITH_FMOD
767                 return FSOUND_IsPlaying(channel);
768                 #else
769                 return false;
770                 #endif
771         }
772         
773         bool isPaused()
774         { 
775 #ifdef WITH_FMOD
776                 return FSOUND_GetPaused(channel);
777 #else
778                 return false;
779 #endif
780         }
781                 
782         
783 public: //forward interface
784         
785         //Accessors for the offset - in seconds
786         const double &get_offset() const {return offset;}
787         void set_offset(const double &d) 
788         {
789                 offset = d;
790         }
791         
792         //Will override the parameter timevalue if the sound is running, and not if it's not...
793         bool get_current_time(double &out)
794         {
795                 if(isRunning())
796                 {
797                         #ifdef WITH_FMOD
798                         unsigned int pos = FSOUND_GetCurrentPosition(channel);
799                         
800                         //adjust back by 1 frame... HACK....
801                         //pos -= FSOUND_DSP_GetBufferLength();
802                         
803                         //set the position
804                         out = pos/(double)sfreq + offset;
805                         #endif
806                         
807                         return true;
808                 }
809                 return false;
810         }
811         
812         //Big implementation functions...
813         bool load(const std::string &filename, const std::string &filedirectory);
814         void clear();
815         
816         //playing functions
817         void play(double t);
818         void stop();
819         
820         //scrubbing functions
821         void start_scrubbing(double t);
822         void scrub(double t);
823         void stop_scrubbing();
824         
825         double scrub_time()
826         {
827                 return curscrubpos;
828         }
829 };
830
831 //--------------- Audio Container definitions --------------------------
832 studio::AudioContainer::AudioContainer()
833 {
834         imp = 0;
835 }
836
837 studio::AudioContainer::~AudioContainer()
838 {
839         if(imp) delete (imp);
840 }
841
842 bool studio::AudioContainer::load(const string &filename,const string &filedirectory)
843 {
844         if(!imp)
845         {
846                 imp = new AudioImp;
847         }
848         
849         profilevalid = false;
850         return imp->load(filename,filedirectory);
851 }
852
853 handle<studio::AudioProfile> studio::AudioContainer::get_profile(float samplerate)
854 {
855         #ifdef WITH_FMOD
856         
857         //if we already have done our work, then we're good
858         if(profilevalid && prof)
859         {
860                 //synfig::info("Using already built profile");
861                 return prof;
862         }
863         
864         //synfig::info("Before profile");
865         //make a new profile at current sample rate
866         
867         //NOTE: We might want to reuse the structure already there...   
868         prof = new AudioProfile;
869         prof->set_parent(this); //Our parent is THIS!!!
870         
871         if(!prof)
872         {
873                 synfig::warning("Couldn't allocate audioprofile...");
874                 return handle<studio::AudioProfile>();
875         }
876         
877         //setting the info for the sample rate
878         //synfig::info("Setting info...");
879         
880         synfig::info("Building Profile...");
881         prof->samplerate = samplerate;
882         if(build_profile(imp->sample,prof->samplerate,prof->samples))
883         {
884                 synfig::info("  Success!");
885                 profilevalid = true;
886                 return prof;
887         }else
888         {
889                 return handle<studio::AudioProfile>();
890         }
891         
892         #else
893         
894         return handle<studio::AudioProfile>();
895         
896         #endif
897 }
898
899 void studio::AudioContainer::clear()
900 {
901         if(imp) 
902         {
903                 delete imp;
904                 imp = 0;
905         }
906         
907         profilevalid = false;
908 }
909         
910 void studio::AudioContainer::play(double t)
911 {
912         if(imp) imp->play(t);
913 }
914
915 void studio::AudioContainer::stop()
916 {
917         if(imp) imp->stop();
918 }
919
920 bool studio::AudioContainer::get_current_time(double &out)
921 {
922         if(imp) return imp->get_current_time(out);
923         else return false;
924 }
925
926 void AudioContainer::set_offset(const double &s)
927 {
928         if(imp) imp->set_offset(s);
929 }
930
931 double AudioContainer::get_offset() const
932 {
933         static double zero = 0;
934         if(imp)
935                 return imp->get_offset();
936         return zero;
937 }
938
939 bool AudioContainer::is_playing() const
940 {
941         if(imp) 
942                 return imp->playing;
943         return false;
944 }
945
946 bool AudioContainer::is_scrubbing() const
947 {
948         if(imp) 
949                 return imp->is_scrubbing();
950         return false;
951 }
952
953 void AudioContainer::start_scrubbing(double t)
954 {
955         if(imp) imp->start_scrubbing(t);
956 }
957
958 void AudioContainer::stop_scrubbing()
959 {
960         if(imp) imp->stop_scrubbing();
961 }
962
963 void AudioContainer::scrub(double t)
964 {
965         if(imp) imp->scrub(t);
966 }
967
968 double AudioContainer::scrub_time() const
969 {
970         if(imp) return imp->scrub_time();
971         else return 0;
972 }
973
974 bool AudioContainer::isRunning() const
975 {
976         if(imp) return imp->isRunning();
977         else return false;
978 }
979
980 bool AudioContainer::isPaused() const
981 {
982         if(imp) return imp->isPaused();
983         else return false;
984 }
985
986 //----------- Audio imp information -------------------
987
988 bool studio::AudioContainer::AudioImp::load(const std::string &filename, 
989                                                                                         const std::string &filedirectory)
990 {
991         clear();
992
993         #ifdef WITH_FMOD
994         
995         //And continue with the sound loading...
996         string  file = filename;
997         
998         //Trivial reject... (fixes stat call problem... where it just looks at directory and not file...)
999         if(file.length() == 0) return false;
1000         
1001         //we don't need the file directory?
1002         if(!is_absolute_path(file))
1003         {
1004                 file=filedirectory+filename;
1005                 synfig::warning("Not absolute hoooray");
1006         }
1007         synfig::info("Loading Audio file: %s", file.c_str());
1008         
1009         //check to see if file exists
1010         {
1011                 struct stat     s;
1012                 if(stat(file.c_str(),&s) == -1 && errno == ENOENT)
1013                 {
1014                         synfig::info("There was no audio file...");                     
1015                         return false;
1016                 }
1017         }
1018         
1019         //load fmod if we can...
1020         //synfig::warning("I'm compiled with FMOD!");
1021         fmodinit.addref();
1022         
1023         //load the stream
1024         int ch = FSOUND_FREE;
1025         FSOUND_SAMPLE *sm = FSOUND_Sample_Load(FSOUND_FREE,file.c_str(),FSOUND_LOOP_OFF|FSOUND_MPEGACCURATE,0,0);
1026         
1027         if(!sm)
1028         {
1029                 synfig::warning("Could not open the audio file as a sample: %s",file.c_str());
1030                 goto error;
1031         }
1032         
1033         //synfig::warning("Opened a file as a sample! :)");
1034         
1035         /*{
1036                 int bufferlen = FSOUND_DSP_GetBufferLength();
1037                 synfig::info("Buffer length = %d samples, %.3lf s",bufferlen, bufferlen / (double)FSOUND_GetOutputRate());
1038         }*/
1039         
1040         //set all the variables since everything has worked out...
1041         //get the length of the stream
1042         {
1043                 length = FSOUND_Sample_GetLength(sm);
1044                 
1045                 int volume = 0;
1046                 FSOUND_Sample_GetDefaults(sm,&sfreq,&volume,0,0);
1047                 
1048                 //double len = length / (double)sfreq;          
1049                 //synfig::info("Sound info: %.2lf s long, %d Hz, %d Vol",(double)length,sfreq,volume);
1050         }
1051         
1052         //synfig::warning("Got all info, and setting up everything, %.2f sec.", length);
1053         //synfig::warning("     BigSample: composed of %d samples", FSOUND_Sample_GetLength(sm));
1054         synfig::info("Successfully opened %s as a sample and initialized it.",file.c_str());
1055         
1056         //set up the playable info
1057         sample = sm;
1058         channel = ch;
1059         
1060         //the length and sfreq params have already been initialized
1061         
1062         return true;
1063         
1064 error:
1065         if(sm) FSOUND_Sample_Free(sm);
1066         file = "";
1067         
1068         fmodinit.decref();
1069         
1070         return false;
1071         
1072         #else
1073         return false;
1074         #endif
1075 }
1076
1077 void studio::AudioContainer::AudioImp::play(double t)
1078 {
1079         #ifdef WITH_FMOD
1080         if(!sample) return;
1081         
1082         //stop scrubbing if we are...
1083         if(is_scrubbing()) stop_scrubbing();
1084         
1085         //t -= offset;
1086         t -= get_offset();
1087         playing = true;
1088         
1089         if(t < 0)
1090         {
1091                 unsigned int timeout = (int)floor(-t * 1000 + 0.5);
1092                 //synfig::info("Playing audio delayed by %d ms",timeout);
1093                 //delay for t seconds...
1094                 delaycon = Glib::signal_timeout().connect(
1095                                                 sigc::mem_fun(*this,&studio::AudioContainer::AudioImp::start_playing_now),timeout);             
1096                 
1097                 init_play();
1098                 FSOUND_SetFrequency(channel,sfreq);
1099                 FSOUND_SetCurrentPosition(channel,0);
1100                 return;
1101         }
1102         
1103         unsigned int position = (int)floor(t*sfreq + 0.5);
1104         
1105         if(position >= FSOUND_Sample_GetLength(sample))
1106         {
1107                 synfig::warning("Can't play audio when past length...");
1108                 return;
1109         }
1110         
1111         init_play();
1112         FSOUND_SetFrequency(channel,sfreq);
1113         FSOUND_SetCurrentPosition(channel,position);
1114         FSOUND_SetPaused(channel,false);
1115         
1116         //synfig::info("Playing audio with position %d samples",position);
1117         
1118         #endif          
1119 }
1120
1121 void studio::AudioContainer::AudioImp::stop()
1122 {
1123         delaycon.disconnect();
1124
1125         #ifdef WITH_FMOD
1126         if(fmodinit.is_loaded() && playing && isRunning())
1127         {               
1128                 FSOUND_SetPaused(channel,true);
1129         }
1130         #endif
1131         
1132         playing = false;
1133 }
1134
1135 void studio::AudioContainer::AudioImp::clear()
1136 {
1137         #ifdef WITH_FMOD
1138         delaycon.disconnect();
1139         
1140         stop();
1141         stop_scrubbing();
1142                         
1143         if(sample)
1144         {
1145                 if(FSOUND_IsPlaying(channel))
1146                 {
1147                         FSOUND_StopSound(channel);
1148                 }
1149                 channel = FSOUND_FREE;
1150                 FSOUND_Sample_Free(sample);
1151                 fmodinit.decref();
1152         }
1153         
1154         playing = false;
1155         
1156         #else
1157         channel = 0;
1158         #endif
1159         
1160         sample = 0;
1161         playing = false;        
1162 }
1163
1164 void AudioContainer::AudioImp::start_scrubbing(double t)
1165 {
1166         //synfig::info("Start scrubbing: %lf", t);
1167         if(playing) stop();
1168                         
1169         set_scrubbing(true);
1170         
1171         #ifdef WITH_FMOD
1172         //make sure the other one is not scrubbing...
1173         if(g_scrubdata.scrub)
1174         {
1175                 *g_scrubdata.scrub = 0; //nullify the pointer...
1176         }
1177         
1178         //Set up the initial state for the delayed audio position
1179         scrinfo.delaystart = 0;
1180         scrinfo.pos = 0;
1181         scrinfo.deltatime = 0;
1182         
1183         //set it to point to our pointer (dizzy...)
1184         g_scrubdata.scrub = &scrptr;
1185
1186         //setup position info so we can know what to do on boundary conditions...
1187         curscrubpos = (t - get_offset()) * sfreq;
1188         
1189         //So we can get an accurate difference...
1190         timer.reset();
1191         
1192         //reposition the sound if it won't be when scrubbed (if it's already in the range...)   
1193         int curi = (int)curscrubpos;
1194         if(curi >= 0 && curi < length)
1195         {
1196                 init_play();
1197                 FSOUND_SetCurrentPosition(channel,curi);
1198                 
1199                 //Set the values...
1200                 scrinfo.pos = curscrubpos;
1201                 scrinfo.delaystart = delay_factor*buffer_length_sec;
1202                                 
1203                 //synfig::info("\tStarting at %d samps, with %d p %.3f delay",
1204                 //                              FSOUND_GetCurrentPosition(channel), (int)scrinfo.pos, scrinfo.delaystart);
1205         }
1206         
1207         
1208         
1209         //enable the dsp...
1210         //synfig::info("\tActivating DSP");
1211         FSOUND_DSP_SetActive(scrubdspunit,true);
1212         #endif
1213 }
1214
1215 void AudioContainer::AudioImp::stop_scrubbing()
1216 {
1217         //synfig::info("Stop scrubbing");
1218         
1219         if(is_scrubbing())
1220         {
1221                 set_scrubbing(false);
1222                 
1223                 #ifdef WITH_FMOD
1224                 g_scrubdata.scrub = 0;
1225         
1226                 //stop the dsp...
1227                 //synfig::info("\tDeactivating DSP");
1228                 FSOUND_DSP_SetActive(scrubdspunit,false);
1229                 if(FSOUND_IsPlaying(channel)) FSOUND_SetPaused(channel,true);
1230                 #endif
1231         }
1232         
1233         curscrubpos = 0;
1234 }
1235
1236 void AudioContainer::AudioImp::scrub(double t)
1237 {
1238         #ifdef WITH_FMOD
1239         //synfig::info("Scrub to %lf",t);
1240         if(is_scrubbing())
1241         {               
1242                 //What should we do?
1243                 
1244                 /* Different special cases
1245                         All outside, all inside,
1246                         coming in (left or right),
1247                         going out (left or right)
1248                 */
1249                 double oldpos = curscrubpos;
1250                 double newpos = (t - get_offset()) * sfreq;
1251                 
1252                 curscrubpos = newpos;
1253                 
1254                 //Ok the sound is running, now we need to tweek it              
1255                 if(newpos > oldpos)
1256                 {
1257                         //Outside so completely stopped...
1258                         if(newpos < 0 || oldpos >= length)
1259                         {
1260                                 //synfig::info("\tOut +");
1261                                 if(FSOUND_IsPlaying(channel))
1262                                 {
1263                                         FSOUND_SetPaused(channel,true);
1264                                 }
1265
1266                                 //Zero out the data!
1267                                 scrinfo.Lock();
1268                                 scrinfo.delaystart = 0;
1269                                 scrinfo.deltatime = 0;
1270                                 scrinfo.Unlock();
1271                                 
1272                                 return;
1273                         }
1274                         
1275                         //going in? - start the sound at the beginning...
1276                         /*else if(oldpos < 0)
1277                         {
1278                                 //Set up the sound to be playing paused at the start...
1279                                 init_play();
1280                                 FSOUND_SetCurrentPosition(channel,0);
1281                                 
1282                                 synfig::info("\tIn + %d", FSOUND_GetCurrentPosition(channel));
1283                                 
1284                                 scrinfo.Lock();
1285                                 scrinfo.pos = 0;
1286                                 scrinfo.delaystart = delay_factor*buffer_length_sec;
1287                                 scrinfo.deltatime = 0;
1288                                 scrinfo.Unlock();
1289                         }*/
1290                         //don't need to deal with leaving... automatically dealt with...
1291                         
1292                         else //We're all inside...
1293                         {
1294                                 //Set new position and decide what to do with time...
1295                                 scrinfo.Lock();
1296                                 scrinfo.pos = newpos;
1297                         
1298                                 //should we restart the delay cycle... (is it done?)                            
1299                                 if(!isRunning() || (scrinfo.delaystart <= 0 && scrinfo.deltatime <= 0 && isPaused()))
1300                                 {
1301                                         //synfig::info("Starting + at %d",(int)newpos);
1302                                         scrinfo.deltatime = 0;
1303                                         scrinfo.delaystart = delay_factor*buffer_length_sec;
1304                                         scrinfo.Unlock();
1305                                         
1306                                         //Set up the sound paused at the current position
1307                                         init_play();
1308                                         int setpos = min(max((int)newpos,0),length);
1309                                         FSOUND_SetCurrentPosition(channel,setpos);
1310                                         timer.reset();
1311                                         return;
1312                                 }
1313                                 
1314                                 //No! just increment the time delta...
1315                                 scrinfo.deltatime += timer.pop_time();
1316                                 
1317                                 //Nope... continue and just increment the deltatime and reset position...
1318                                 scrinfo.Unlock();
1319                                                         
1320                                 //set channel and unpause
1321                                 FSOUND_SetPaused(channel,false);
1322                                 scrinfo.channel = channel;
1323                                                                 
1324                         }
1325                 }else if(newpos < oldpos)
1326                 {
1327                         //completely stopped...
1328                         if(newpos >= length || oldpos < 0)
1329                         {
1330                                 //synfig::info("Out -");
1331                                 if(FSOUND_IsPlaying(channel))
1332                                 {
1333                                         FSOUND_SetPaused(channel,true);
1334                                 }
1335                                 
1336                                 //Zero out the data!
1337                                 scrinfo.Lock();
1338                                 scrinfo.delaystart = 0;
1339                                 scrinfo.deltatime = 0;
1340                                 scrinfo.Unlock();
1341                         }
1342                         
1343                         //going in? - start going backwards at the end...
1344                         /*else if(oldpos >= length)
1345                         {
1346                                 synfig::info("In -");
1347                                 //Set up the sound to be playing paused at the start...
1348                                 init_play();
1349                                 FSOUND_SetCurrentPosition(channel,length-1);                            
1350                                 
1351                                 scrinfo.Lock();
1352                                 scrinfo.pos = length-1;
1353                                 scrinfo.delaystart = delay_factor*buffer_length_sec;
1354                                 scrinfo.deltatime = 0;
1355                                 scrinfo.Unlock();
1356                         }*/
1357                         //we don't have to worry about the leaving case...
1358                         
1359                         else //We're all inside...
1360                         {
1361                                 //Set new position and decide what to do with time...
1362                                 scrinfo.Lock();
1363                                 scrinfo.pos = newpos;
1364                         
1365                                 //should we restart the delay cycle... (is it done?)                            
1366                                 if(!isRunning() ||(scrinfo.delaystart <= 0 && scrinfo.deltatime <= 0 && isPaused()))
1367                                 {
1368                                         //synfig::info("Starting - at %d",(int)newpos);
1369                                         scrinfo.deltatime = 0;
1370                                         scrinfo.delaystart = delay_factor*buffer_length_sec;
1371                                         scrinfo.Unlock();
1372                                         
1373                                         //reset timing so next update will be a valid diff... 
1374                                         init_play();
1375                                         int setpos = min(max((int)newpos,0),length);
1376                                         FSOUND_SetCurrentPosition(channel,setpos);
1377                                         timer.reset();
1378                                         return;
1379                                 }
1380                                 
1381                                 //No! just increment the time delta...
1382                                 scrinfo.deltatime += timer.pop_time();
1383                                 
1384                                 //Nope... continue and just increment the deltatime and reset position...
1385                                 scrinfo.Unlock();
1386                                                         
1387                                 //set channel and unpause
1388                                 FSOUND_SetPaused(channel,false);
1389                                 scrinfo.channel = channel;
1390                         }
1391                 }
1392         }
1393         #endif
1394 }