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