1 // osziview.cc
2 //
3 //    oszi - widget for gtk--
4 //
5 //    Copyright (C) 1999  Florian Berger
6 //    Email: florian.berger@jk.uni-linz.ac.at
7 //
8 //    This program is free software; you can redistribute it and/or modify
9 //    it under the terms of the GNU General Public License Version 2 as
10 //    published by the Free Software Foundation;
11 //
12 //    This program is distributed in the hope that it will be useful,
13 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //    GNU General Public License for more details.
16 //
17 //    You should have received a copy of the GNU General Public License
18 //    along with this program; if not, write to the Free Software
19 //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 //
22 
23 #include <stdio.h>
24 #include <string.h>
25 
26 #include <math.h>
27 
28 #include "resources.h"
29 
30 #include "osziview.h"
31 
32 #include <gtkmm/style.h>
33 #include <gdkmm/color.h>
34 #include <math.h>
35 
36 #define SCALE_HEIGHT 20
37 
38 #define xscr 20
39 #define yscr 5
40 #define wscr (get_width()-xscr*2)
41 #define hscr (get_height()-yscr-SCALE_HEIGHT-1)
42 
OsziView()43 OsziView::OsziView() : Gtk::DrawingArea()
44 {
45     samp_byte_array = g_byte_array_new();
46 
47     i_col_bg.   set("black");         get_colormap()->alloc_color(i_col_bg   );
48     i_col_trig. set("cyan" );         get_colormap()->alloc_color(i_col_trig );
49     i_col_mark. set("red"  );         get_colormap()->alloc_color(i_col_mark );
50     i_col_data. set("green");         get_colormap()->alloc_color(i_col_data );
51     i_col_scale.set("#B8B8B8");       get_colormap()->alloc_color(i_col_scale);
52     i_col_ticks.set("#666");          get_colormap()->alloc_color(i_col_ticks);
53     i_col_zero. set("gray" );         get_colormap()->alloc_color(i_col_zero );
54     trigfact=0.6;
55     i_adaptive_scale=1;
56     freq=0.0;
57     startpoint=0;
58     endpoint=0;
59 
60     // Default to three dummy samples
61     setSampleData((const unsigned char *) "\0\0\0", 3);
62 }
63 
64 
~OsziView()65 OsziView::~OsziView()
66 {
67     g_byte_array_free (samp_byte_array, TRUE);
68 }
69 
calc_minmax(void)70 void OsziView::calc_minmax(void)
71 {
72     int i;
73     unsigned char * samp_uc;
74     short int *     samp_s16le;
75 
76     samp_s16le =     (short int *) samp;
77     samp_uc    = (unsigned char *) samp;
78 
79     if(i_sampfmt == U8){
80         i_minsamp=255;
81         i_maxsamp=0;
82         for(i=0;i<sampnr;i++){
83             if ( samp_uc[i-1] > i_maxsamp ) i_maxsamp = samp_uc[i-1];
84             if ( samp_uc[i-1] < i_minsamp ) i_minsamp = samp_uc[i-1];
85         }
86         if ( !i_adaptive_scale ){
87             i_divisor = 0x100;
88         } else {
89             i_divisor = 8;
90             if ( 2*Abs(i_maxsamp-128) > i_divisor ) i_divisor=2*Abs(i_maxsamp-128);
91             if ( 2*Abs(i_minsamp-128) > i_divisor ) i_divisor=2*Abs(i_minsamp-128);
92         }
93     } else if(i_sampfmt == S16_LE){
94         i_minsamp= 33000;
95         i_maxsamp=-33000;
96         for(i=0;i<sampnr;i++){
97             if ( samp_s16le[i-1] > i_maxsamp ) i_maxsamp = samp_s16le[i-1];
98             if ( samp_s16le[i-1] < i_minsamp ) i_minsamp = samp_s16le[i-1];
99         }
100         if ( !i_adaptive_scale ){
101             i_divisor = 0x10000;
102         } else {
103             i_divisor = 2048;
104             if ( 2*Abs(i_maxsamp) > i_divisor ) i_divisor=2*Abs(i_maxsamp);
105             if ( 2*Abs(i_minsamp) > i_divisor ) i_divisor=2*Abs(i_minsamp);
106         }
107     }
108 }
109 
calc_freq(void)110 void OsziView::calc_freq(void)
111 /* in units of sampfreq */
112 {
113     int t1, t2, A1, A2, tc, i, schmitt_triggered;
114     short int *     samp_s16le;
115     unsigned char * samp_uc;
116 
117     samp_s16le =     (short int *) samp;
118     samp_uc    = (unsigned char *) samp;
119 
120     if(i_sampfmt==U8){
121         for(i=0,A1=0;i<sampnr;i++)
122             if (A1<Abs(samp_uc[i]-128) && samp_uc[i]-128>0) A1=Abs(samp_uc[i]-128);
123         for(i=0,A2=0;i<sampnr;i++)
124             if (A2<Abs(samp_uc[i]-128) && samp_uc[i]-128<0) A2=Abs(samp_uc[i]-128);
125     } else if(i_sampfmt==S16_LE){
126         for(i=0,A1=0;i<sampnr;i++)
127             if (A1<Abs(samp_s16le[i]) && samp_s16le[i]>0) A1=Abs(samp_s16le[i]);
128         for(i=0,A2=0;i<sampnr;i++)
129             if (A2<Abs(samp_s16le[i]) && samp_s16le[i]<0) A2=Abs(samp_s16le[i]);
130     }
131 //      A1 = (int)( (double)A*M_PI/2.0/(double)sampnr+0.5 );
132 //      A1 = (int)( (double)A*M_PI/2.0/(double)sampnr+0.5 );
133     if(i_sampfmt==U8){
134         t1 = 128 + (int)( A1 * trigfact + 0.5 );
135         t2 = 128 - (int)( A2 * trigfact + 0.5 );
136     } else if(i_sampfmt==S16_LE){
137         t1 =   (int)( A1 * trigfact + 0.5 );
138         t2 = - (int)( A2 * trigfact + 0.5 );
139     }
140     startpoint=0;
141     if(i_sampfmt==U8){
142         for( i=1; samp_uc[i]<=t1 && i<sampnr; i++ );
143         for( ; !LEVTRIGN(samp_uc,i,t2) && i<sampnr; i++ );
144     } else if(i_sampfmt==S16_LE){
145         for( i=1; samp_s16le[i]<=t1 && i<sampnr; i++ );
146         for( ; !LEVTRIGN(samp_s16le,i,t2) && i<sampnr; i++ );
147     }
148     startpoint=i;
149     schmitt_triggered=NO;
150     endpoint=startpoint+1;
151     tc=0;
152     if(i_sampfmt==U8){
153         for( i=startpoint, tc=0; i<sampnr; i++ ) {
154             if( !schmitt_triggered )
155                 schmitt_triggered = (samp_uc[i]>=t1);
156             else if( LEVTRIGN(samp_uc,i,t2) ) {
157                 endpoint=i; tc++;
158                 schmitt_triggered = NO;
159             }
160         }
161     } else if(i_sampfmt==S16_LE){
162         for( i=startpoint, tc=0; i<sampnr; i++ ) {
163             if( !schmitt_triggered )
164                 schmitt_triggered = (samp_s16le[i]>=t1);
165             else if( LEVTRIGN(samp_s16le,i,t2) ) {
166                 endpoint=i; tc++;
167                 schmitt_triggered = NO;
168             }
169         }
170     }
171     if (endpoint==startpoint) endpoint++;
172     freq = (double)tc/(double)(endpoint-startpoint);
173     if (freq<1E-15) freq = 1E-15;
174 
175     trigger_a = t1;
176     trigger_b = t2;
177 }
178 
recalc(void)179 void OsziView::recalc(void)
180 {
181     calc_minmax();
182     calc_freq();
183 }
184 
paint_sample(void)185 void OsziView::paint_sample(void)
186 {
187     int i,x1,x2,y1,y2;
188     unsigned char * samp_uc;
189     short int *     samp_s16le;
190     //erase();
191     samp_s16le =     (short int *) samp;
192     samp_uc    = (unsigned char *) samp;
193 
194     if (!i_GC)
195 	i_GC = Gdk::GC::create( get_window() );
196 
197     i_GC->set_foreground(i_col_zero);
198     get_window()->
199         draw_line( i_GC, xscr,yscr+hscr/2,xscr+wscr-1,yscr+hscr/2);
200 
201     i_GC->set_foreground(i_col_data);
202     for(i=1;i<sampnr;i++){
203         x1=xscr+(i-1)*wscr/sampnr;
204         x2=xscr+i*wscr/sampnr;
205         if(i_sampfmt == U8){
206             y1=yscr+(samp_uc[i-1]-128+i_divisor/2)*hscr/i_divisor;
207             y2=yscr+(samp_uc[i]-128+i_divisor/2)*hscr/i_divisor;
208         } else if(i_sampfmt == S16_LE){
209             y1=yscr+((int)samp_s16le[i-1]+i_divisor/2)*hscr/i_divisor;
210             y2=yscr+((int)samp_s16le[i]  +i_divisor/2)*hscr/i_divisor;
211         }
212         get_window()->draw_line( i_GC,  x1, y1, x2, y2 );
213     }
214     //printf("hallo\n");
215 }
216 
paint_marks(void)217 void OsziView::paint_marks(void)
218 {
219     int linex, liney;
220     int t1 = trigger_a, t2 = trigger_b;
221 
222     i_GC->set_foreground(i_col_mark);
223     linex = xscr+endpoint*wscr/sampnr;
224     get_window()->draw_line( i_GC, linex, yscr, linex, yscr+hscr );
225     linex = xscr+startpoint*wscr/sampnr;
226     get_window()->draw_line( i_GC, linex, yscr, linex, yscr+hscr );
227 
228     i_GC->set_foreground(i_col_trig);
229 
230     if (i_sampfmt == U8)
231     {
232 	t1 -= 128;
233 	t2 -= 128;
234     }
235 
236     liney = yscr+(t1+i_divisor/2)*hscr/i_divisor;
237     get_window()->draw_line( i_GC, xscr, liney, xscr+wscr, liney );
238     liney = yscr+(t2+i_divisor/2)*hscr/i_divisor;
239     get_window()->draw_line( i_GC, xscr, liney, xscr+wscr, liney );
240 }
241 
copy_sample_data(const void * ptr,int data_size)242 void OsziView::copy_sample_data(const void *ptr, int data_size)
243 {
244     g_byte_array_set_size(samp_byte_array, 0);
245     g_byte_array_append(samp_byte_array, (guint8 *) ptr, data_size);
246     samp = (short int *) samp_byte_array->data;
247 }
248 
setSampleData(const unsigned char * s,int num_samples)249 void OsziView::setSampleData(const unsigned char *s, int num_samples)
250 {
251     copy_sample_data(s, num_samples);
252     i_sampfmt = U8;
253     if (num_samples != sampnr)
254     {
255 	sampnr = num_samples;
256 	invalidate();
257     }
258     else
259 	invalidate_sample();
260     recalc();
261 }
262 
setSampleData(const short int * s,int num_samples)263 void OsziView::setSampleData(const short int *s, int num_samples)
264 {
265     copy_sample_data(s, num_samples * sizeof (short int));
266     i_sampfmt = S16_LE;
267     if (num_samples != sampnr)
268     {
269 	sampnr = num_samples;
270 	invalidate();
271     }
272     else
273 	invalidate_sample();
274     recalc();
275 }
276 
setSampleFreq(double f)277 void OsziView::setSampleFreq(double f) { sampfreq = f; invalidate(); }
278 
279 
setAdaptive(int active)280 void OsziView::setAdaptive(int active) { i_adaptive_scale = active; invalidate(); }
281 
282 
setTrigFact(double fact)283 void OsziView::setTrigFact(double fact) { trigfact=fact; invalidate(); }
284 
285 
getTrigFact()286 double OsziView::getTrigFact() { return(trigfact); }
287 
invalidate(void)288 void OsziView::invalidate(void)
289 {
290     // Invalidate the whole window
291     if (is_realized())
292     {
293 	Gdk::Rectangle rect = get_allocation();
294 	rect.set_x(0);
295 	rect.set_y(0);
296 	// The gtkmm bindings won't let you call invalidate_rect
297 	// with a NULL argument to clear the whole window, so you
298 	// have to jump through these hoops
299 	get_window()->invalidate_rect(rect, false);
300     }
301 }
302 
invalidate_sample(void)303 void OsziView::invalidate_sample(void)
304 {
305     if (is_realized())
306     {
307 	Gdk::Rectangle rect = get_allocation();
308 	rect.set_x(0);
309 	rect.set_y(0);
310 	rect.set_height(rect.get_height() - SCALE_HEIGHT);
311 	get_window()->invalidate_rect(rect, false);
312     }
313 }
314 
paint_scale()315 void OsziView::paint_scale()
316 {
317     double i,j;
318     double di,dj;
319     double dim; //mantissa of di
320     char str[100];
321 //       printf("nr=%d, freq=%lf\n",nr,f);
322 
323     if (!i_GC)
324         i_GC = Gdk::GC::create( get_window() );
325 
326     di = (double)sampnr/sampfreq/10.0;
327     for(dim=di;dim>=10.0;dim/=10.0);
328     for(;dim<1.0;dim*=10.0);
329     if (dim<=5.0)
330     { di=di/dim*5.0; dim=5.0; }
331     else
332     { di=di/dim*10.0; dim=10.0;}
333     dj=di/10.0;
334 
335     int scale_top = get_height() - SCALE_HEIGHT;
336 
337     i_GC->set_foreground(i_col_ticks);
338     for( j=0.0; j<(double)sampnr/sampfreq; j+=dj ){
339         get_window()->
340             draw_line( i_GC, xscr+(j*sampfreq)*wscr/sampnr,scale_top,
341                              xscr+(j*sampfreq)*wscr/sampnr,scale_top+3 );
342     }
343 
344     if (dim!=5.0){
345         dj=di/2.0;
346         i_GC->set_foreground(i_col_scale);
347         for( j=0.0; j<(double)sampnr/sampfreq; j+=dj ){
348             get_window()->
349                 draw_line( i_GC, xscr+(j*sampfreq)*wscr/sampnr,scale_top,
350                                  xscr+(j*sampfreq)*wscr/sampnr,scale_top+5 );
351         }
352     }
353 
354     i_GC->set_foreground(i_col_scale);
355 
356 
357     Glib::RefPtr<Pango::Layout> layout = create_pango_layout("");
358     layout->set_font_description( Pango::FontDescription("Sans 8 ") );
359 
360     for( i=0.0; i<(double)sampnr/sampfreq; i+=di ){
361 
362         get_window()->
363             draw_line( i_GC, xscr+(i*sampfreq)*wscr/sampnr,scale_top,
364                              xscr+(i*sampfreq)*wscr/sampnr,scale_top+7 );
365 
366         sprintf(str,"%.0f",i*1000); // in ms
367 	layout->set_text( str );
368 	Pango::Rectangle extents = layout->get_pixel_logical_extents();
369 
370         get_window()->
371             draw_layout(i_GC,
372                         xscr+(i*sampfreq)*wscr/sampnr-
373                         extents.get_width()/2,
374                         scale_top+7+extents.get_ascent(),
375                         layout
376                        );
377     }
378 }
379 
380 
on_expose_event(GdkEventExpose * event)381 bool OsziView::on_expose_event(GdkEventExpose *event)
382 {
383     Gtk::DrawingArea::on_expose_event(event);
384 
385     // Avoid painting the scale if only the sample area has changed
386     if (event->area.y + event->area.height > get_height() - SCALE_HEIGHT)
387 	paint_scale();
388 
389     paint_sample();
390     paint_marks();
391 
392     return true;
393 }
394 
on_realize()395 void OsziView::on_realize()
396 {
397     Gtk::DrawingArea::on_realize();
398 
399     get_window()->set_background( i_col_bg );
400 }
401