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