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