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