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