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