Fix bugs in previous commit that caused FTBFS in synfig and ETL FTBFS with older...
[synfig.git] / synfig-studio / tags / synfigstudio_0_61_05 / synfig-studio / src / gtkmm / widget_sound.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file widget_sound.cpp
3 **      \brief Widget Sound Implementation File
4 **
5 **      $Id: widget_sound.cpp,v 1.1.1.1 2005/01/07 03:34:37 darco Exp $
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 <gtkmm/adjustment.h>
33
34 #include <synfig/general.h>
35 #include <ETL/clock>
36
37 #include "widget_sound.h"
38 #include "audiocontainer.h"
39
40 #endif
41
42 /* === U S I N G =========================================================== */
43
44 using namespace std;
45 using namespace etl;
46 //using namespace synfig;
47
48 using studio::AudioProfile;
49
50 /* === M A C R O S ========================================================= */
51
52 /* === G L O B A L S ======================================================= */
53
54 /* === P R O C E D U R E S ================================================= */
55
56 /* === M E T H O D S ======================================================= */
57
58 /* === E N T R Y P O I N T ================================================= */
59         
60 studio::Widget_Sound::Widget_Sound()
61 {
62 }
63
64 studio::Widget_Sound::~Widget_Sound()
65 {
66 }
67
68 void studio::Widget_Sound::set_position(double t)
69 {
70         //synfig::info("Setting position to %.2lf s", t);
71         if(adj_timescale && t != adj_timescale->get_value())
72         {
73                 float upper = adj_timescale->get_upper();
74                 float lower = adj_timescale->get_lower();
75                 float framesize =  upper - lower;
76                 
77                 if(t < lower)
78                 {
79                         lower -= ceil((lower-t)/framesize)*framesize;
80                         upper = lower + framesize;
81                         adj_timescale->set_lower(lower); adj_timescale->set_upper(upper);
82                         adj_timescale->set_value(t);
83                         adj_timescale->changed(); adj_timescale->value_changed(); 
84                 }else
85                 if(t > upper)
86                 {
87                         lower += ceil((t-upper)/framesize)*framesize;
88                         upper = lower + framesize;
89                         adj_timescale->set_lower(lower); adj_timescale->set_upper(upper);
90                         adj_timescale->set_value(t);
91                         adj_timescale->changed(); adj_timescale->value_changed();
92                 }else
93                 {
94                         adj_timescale->set_value(t);
95                         adj_timescale->value_changed();
96                 }
97         }
98 }
99
100 double studio::Widget_Sound::get_position() const
101 {
102         if(adj_timescale)
103         {
104                 return adj_timescale->get_value();
105         }
106         return 0;
107 }
108
109 bool studio::Widget_Sound::set_profile(etl::handle<AudioProfile>        p)
110 {
111         clear();
112
113         //set the profile
114         audioprof = p;
115         
116         if(!audioprof)
117         {
118                 clear();
119                 return false;
120         }
121         
122         return true;
123 }
124
125 etl::handle<AudioProfile>       studio::Widget_Sound::get_profile() const
126 {
127         return audioprof;
128 }
129
130 void studio::Widget_Sound::clear()
131 {
132         audioprof.detach();
133 }
134
135 void studio::Widget_Sound::draw()
136 {
137         on_expose_event();
138 }
139
140 bool studio::Widget_Sound::on_expose_event(GdkEventExpose *heh)
141 {       
142         if(!get_window()) return false; 
143
144         //clear the background to dark grey
145         Glib::RefPtr<Gdk::GC>   gc = Gdk::GC::create(get_window());
146         
147         if(!gc) return false;
148
149         {
150                 Gdk::Rectangle r(0,0,get_width(),get_height());
151                 get_window()->begin_paint_rect(r);
152         }
153         Gdk::Color      c("#3f3f3f");   
154         gc->set_rgb_fg_color(c);
155         gc->set_background(c);
156
157         int w = get_width();
158         int baseline = get_height()/2;
159         get_window()->draw_rectangle(gc,true,0,0,w,get_height());
160         
161         //set up the color to be blue
162         c.set_rgb_p(0,0.5,1);
163         gc->set_rgb_fg_color(c);
164         
165         //draw the base line
166         get_window()->draw_line(gc,0,baseline,w,baseline);
167         
168         //redraw all the samples from begin to end, but only if we have samples to draw (or there is no space to draw)
169         
170         //synfig::warning("Ok rendered everything, now must render actual sound wave");
171         if(!audioprof || !adj_timescale || !w) 
172         {
173                 get_window()->end_paint();
174                 return true;
175         }
176         
177         //draw you fool!
178         float framesize = adj_timescale->get_upper() - adj_timescale->get_lower();
179         if(framesize)
180         {
181                 float delta=0,cum=0;
182                 
183                 //position in sample space
184                 int begin=0,end=0;
185                 int     cur=0,maxs=0,mins=0;
186                 
187                 int       i=0; //pixel counter
188                 
189                 //etl::clock    check; check.reset();
190                 
191                 float position = adj_timescale->get_value();
192                 float samplerate = audioprof->get_samplerate();
193                 int             posi = 0;               
194                 //enforce position inside of frame size 
195                 {
196                         float offset = audioprof->get_offset();
197                 
198                         //clamp begin and end to framesize
199                         float beginf = adj_timescale->get_lower();
200                         float endf = adj_timescale->get_upper();
201                         
202                         posi = round_to_int((position-beginf)*w/framesize);
203                         //posi = (int)((position-beginf)*w/framesize);
204                         
205                         //calculate in sample space from seconds
206                         begin = round_to_int((beginf - offset)*samplerate);
207                         end = round_to_int((endf - offset)*samplerate);
208                         //begin = (int)((beginf - offset)*samplerate);
209                         //end = (int)((endf - offset)*samplerate);
210                 }
211                 
212                 delta = (end - begin)/(float)w; //samples per pixel
213                 
214                 /*synfig::warning("Rendering a framesize of %f secs from [%d,%d) samples to %d samples, took %f sec", 
215                                                 framesize, begin, end, w, check());*/
216                 
217                 cur = begin;
218                 i = 0; cum = 0;
219                 for(int i=0;i<w;++i)
220                 {
221                         //get the maximum of the collected samples
222                         maxs = 0;
223                         mins = 0;
224                         for(;cum < delta; ++cum, ++cur)
225                         {
226                                 maxs = std::max(maxs,(int)(*audioprof)[cur]);
227                                 mins = std::min(mins,(int)(*audioprof)[cur]);
228                         }
229                         cum -= delta;
230                         
231                         //draw spike if not needed be
232                         if(maxs||mins)
233                         {
234                                 int top = maxs * baseline / 64;
235                                 int bot = mins * baseline / 64;
236                                 
237                                 get_window()->draw_line(gc,i,baseline+bot,i,baseline+top);
238                         }
239                 }
240                 
241                 //synfig::warning("Drawing audio line");
242                 c.set_rgb_p(1,0,0);
243                 gc->set_rgb_fg_color(c);
244                 get_window()->draw_line(gc,posi,0,posi,get_height());
245         }
246         get_window()->end_paint();
247                 
248         return true;
249 }
250
251 //--- Handle the single clicking and dragging for scrubbing
252
253 bool studio::Widget_Sound::on_motion_notify_event(GdkEventMotion* event)
254 {
255         Gdk::ModifierType       mod = Gdk::ModifierType(event->state);
256
257         //if we are scrubbing
258         if(mod & Gdk::BUTTON1_MASK)
259         {
260                 //Can't do this if we don't have a time frame (heheh...)
261                 if(!adj_timescale) return false;
262                         
263                 double beg = adj_timescale->get_lower(), end = adj_timescale->get_upper();
264                 
265                 //find event position in time
266                 double t = beg + event->x * (end-beg) / get_width();
267
268                 //signal that we are scrubbing to this new value...
269                 signal_scrub()(t);
270                                 
271                 
272                 // We should be able to just call
273                 // Widget_Timeslider::on_motion_notify_event(),
274                 // but that seems to cause the program to halt
275                 // for some reason. So for now, let's do the job ourselves
276                 //adj_timescale->set_value(t);
277                 //adj_timescale->changed();
278                 //return true;
279         }
280         
281         return Widget_Timeslider::on_motion_notify_event(event);
282 }
283
284 bool studio::Widget_Sound::on_button_press_event(GdkEventButton *event)
285 {
286         //Assume button PRESS
287         
288         //if we are starting... using left click
289         if(event->button == 1)
290         {
291                 if(!adj_timescale) return false;
292                         
293                 double beg = adj_timescale->get_lower(), end = adj_timescale->get_upper();
294                 
295                 //find event position in time
296                 double t = beg + event->x * (end-beg) / get_width();
297
298                 //signal the attached scrubbing devices...
299                 signal_start_scrubbing()(t);
300                                 
301                 return true;
302         }
303         
304         return Widget_Timeslider::on_button_press_event(event);
305 }
306
307 bool studio::Widget_Sound::on_button_release_event(GdkEventButton *event)
308 {
309         //Assume button RELEASE
310         
311         //if we are ending... using left click
312         if(event->button == 1)
313         {
314                 //signal the scrubbing device... to stop
315                 signal_stop_scrubbing()();
316                                 
317                 return true;
318         }
319         
320         return Widget_Timeslider::on_button_release_event(event);
321 }