More valgrinding and fixing a serious mem leak - Alex
[terminatorX.git] / src / tX_widget.c
1 /*
2     terminatorX - realtime audio scratching software
3     Copyright (C) 1999-2003  Alexander K├Ânig
4  
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9  
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14  
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  
19     File: tX_widget.c
20  
21     Description: This contains the implementation of the tx_widget.
22                  This file is based on the GTK+ widget example from
23                  the GTK+ 1.2 tutorial.
24 */
25
26 #include <math.h>
27
28 #include <gtk/gtkwindow.h>
29 #include "tX_widget.h"
30 #include "tX_types.h"
31 #include "tX_global.h"
32 #include <malloc.h>
33 #include <stdlib.h>
34
35 #ifndef WIN32
36 #include <unistd.h>
37 #endif
38
39 #define TX_DEFAULT_SIZE_X 100
40 #define TX_DEFAULT_SIZE_Y 30
41
42 /* forward declaration */
43 static void gtk_tx_class_init(GtkTxClass *);
44 static void gtk_tx_init(GtkTx * tx);
45 GtkWidget *gtk_tx_new(int16_t * wavdata, int wavsamples);
46 static void gtk_tx_destroy(GtkObject * object);
47 void gtk_tx_set_data(GtkTx * tx, int16_t * wavdata, int wavsamples);
48 static void gtk_tx_realize(GtkWidget * widget);
49 static void gtk_tx_size_request(GtkWidget * widget, GtkRequisition * requisition);
50 static void gtk_tx_size_allocate(GtkWidget * widget, GtkAllocation * allocation);
51 static gint gtk_tx_expose(GtkWidget * widget, GdkEventExpose * event);
52 static void gtk_tx_update(GtkTx * tx);
53 static void gtk_tx_prepare(GtkWidget * widget);
54
55 /* data */
56 static GtkWidgetClass *parent_class = NULL;
57
58 /* widget "methods" */
59
60 guint gtk_tx_get_type() {
61         static guint tx_type = 0;
62         
63         if (!tx_type) {
64                 GtkTypeInfo tx_info = {
65                 "GtkTx",
66                 sizeof(GtkTx),
67                 sizeof(GtkTxClass),
68                 (GtkClassInitFunc) gtk_tx_class_init,
69                 (GtkObjectInitFunc) gtk_tx_init,
70                 /* reserved */ NULL,
71                 /* reserved */ NULL,
72                 /* reserved */ NULL
73         };
74         
75          tx_type = gtk_type_unique(gtk_widget_get_type(), &tx_info);
76         }
77         
78         return tx_type;
79 }
80
81 static void gtk_tx_class_init(GtkTxClass * gclass) {
82         GtkObjectClass *object_class;
83         GtkWidgetClass *widget_class;
84         
85         object_class = (GtkObjectClass *) gclass;
86         widget_class = (GtkWidgetClass *) gclass;
87         
88         parent_class = gtk_type_class(gtk_widget_get_type());
89         
90         object_class->destroy = gtk_tx_destroy;
91         
92         widget_class->realize = gtk_tx_realize;
93         widget_class->expose_event = gtk_tx_expose;
94         widget_class->size_request = gtk_tx_size_request;
95         widget_class->size_allocate = gtk_tx_size_allocate;
96 //      widget_class->button_press_event = gtk_tx_button_press;
97 //      widget_class->button_release_event = gtk_tx_button_release;
98 //      widget_class->motion_notify_event = gtk_tx_motion_notify;
99 }
100
101 void gtk_tx_mk_col(GtkTx * tx, GdkColor * col, float r, float g, float b) {
102         float max = 65535.0;
103         
104         col->red = (gint) (r * max);
105         col->green = (gint) (g * max);
106         col->blue = (gint) (b * max);
107                 
108         gdk_colormap_alloc_color(gtk_widget_get_colormap(GTK_WIDGET(tx)), col, 0, 1);
109 }
110
111 static void gtk_tx_init(GtkTx * tx) {
112         GdkColormap *priv;
113
114         tx->disp_data = NULL;
115         tx->data = NULL;
116         tx->samples = 0;
117         tx->do_showframe = 0;
118 #ifdef USE_DISPLAY_NORMALIZE
119         tx->max_value=-1;
120 #endif
121         
122         priv = gdk_colormap_new(gtk_widget_get_visual(GTK_WIDGET(tx)), 6);
123         gtk_widget_set_colormap(GTK_WIDGET(tx), priv);
124         
125         gtk_tx_mk_col(tx, &tx->bg, 0, 0, 0);
126         gtk_tx_mk_col(tx, &tx->fg, 0, 1, 0);
127         gtk_tx_mk_col(tx, &tx->focus_fg, 1, 1, 0);
128         gtk_tx_mk_col(tx, &tx->focus_bg, 0, 0, 0.3);    
129         
130         gtk_tx_mk_col(tx, &tx->busy, 1, 0.4, 0.4);
131         gtk_tx_mk_col(tx, &tx->mute, 1, 1, 1);
132         
133         tx->current_fg=&tx->fg;
134         tx->current_bg=&tx->bg;
135         
136         tx->spp=1;
137         tx->lastmute=-1;
138         tx->zoom=0;
139         tx->cursor_pos=0;
140         tx->cursor_x_pos=0;
141 }
142
143 GtkWidget *gtk_tx_new(int16_t * wavdata, int wavsamples) {
144         GtkTx *tx;
145
146         tx = gtk_type_new(gtk_tx_get_type());
147
148         tx->data = wavdata;
149         tx->samples = wavsamples;
150         tx->zoom=0;
151         tx->display_x_offset=0;
152         
153         return GTK_WIDGET(tx);
154 }
155
156 static void gtk_tx_destroy(GtkObject * object) {
157         g_return_if_fail(object != NULL);
158         g_return_if_fail(GTK_IS_TX(object));
159
160         GtkTx *tx=GTK_TX(object);
161         
162         if (tx->disp_data) { free(tx->disp_data); tx->disp_data=NULL; }
163         
164         if (GTK_OBJECT_CLASS(parent_class)->destroy) {
165                 (*GTK_OBJECT_CLASS(parent_class)->destroy) (object);
166         }       
167 }
168
169 #define MAX_ZOOM_WIDTH 500000.0
170
171 void gtk_tx_set_data(GtkTx * tx, int16_t * wavdata, int wavsamples) {
172         g_return_if_fail(tx != NULL);
173         g_return_if_fail(GTK_IS_TX(tx));
174
175         tx->data = wavdata;
176         tx->samples = wavsamples;
177 #ifdef USE_DISPLAY_NORMALIZE    
178         tx->max_value=-1;
179 #endif
180         
181         gtk_tx_prepare(GTK_WIDGET(tx));
182         gtk_tx_update(tx);
183 }
184
185 static void gtk_tx_realize(GtkWidget * widget) {
186         GtkTx *tx;
187         GdkWindowAttr attributes;
188         gint attributes_mask;
189
190         g_return_if_fail(widget != NULL);
191         g_return_if_fail(GTK_IS_TX(widget));
192
193         GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
194         tx = GTK_TX(widget);
195
196         attributes.x = widget->allocation.x;
197         attributes.y = widget->allocation.y;
198         attributes.width = widget->allocation.width;
199         attributes.height = widget->allocation.height;
200         attributes.wclass = GDK_INPUT_OUTPUT;
201         attributes.window_type = GDK_WINDOW_CHILD;
202         attributes.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK;
203         attributes.visual = gtk_widget_get_visual(widget);
204         attributes.colormap = gtk_widget_get_colormap(widget);
205         attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
206
207         widget->window = gdk_window_new(widget->parent->window, &attributes, attributes_mask);
208         widget->style = gtk_style_attach(widget->style, widget->window);
209
210         gdk_window_set_user_data(widget->window, widget);
211
212         gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
213 }
214
215 static void gtk_tx_size_request(GtkWidget * widget, GtkRequisition * requisition) {
216         requisition->width = TX_DEFAULT_SIZE_X;
217         requisition->height = TX_DEFAULT_SIZE_Y;
218 }
219
220 static void gtk_tx_prepare(GtkWidget * widget) {
221         int x, sample;
222         int16_t *ptr;
223         f_prec value;
224         GtkTx *tx;
225         int avg_pos;
226         
227         g_return_if_fail(widget != NULL);
228         g_return_if_fail(GTK_IS_TX(widget));
229
230         tx = GTK_TX(widget);
231         
232         tx->yc = widget->allocation.height / 2;
233
234         if (tx->disp_data) { free(tx->disp_data); tx->disp_data=NULL; }
235
236         if (tx->data) {
237                 int max_spp=tx->samples/widget->allocation.width;
238                 int min_spp=tx->samples/MAX_ZOOM_WIDTH;
239                 gdouble diff;
240                 
241                 if (min_spp==0) min_spp=1;
242                 
243                 diff=max_spp-min_spp;
244                 
245                 tx->spp=min_spp+diff*(1.0-tx->zoom);
246                 tx->display_width = tx->samples/tx->spp;
247                 
248 #ifdef USE_DISPLAY_NORMALIZE    
249                 tx->max_value=-1;
250 #endif          
251                 
252             tx->disp_data = (int16_t *) malloc(tx->display_width * sizeof(int16_t));
253
254             if (tx->disp_data) {                        
255 #ifdef USE_DISPLAY_NORMALIZE            
256                         if (tx->max_value==-1) {
257                                 /* We haven't figured a max value yet... */
258                                 //puts("searching max...");
259                 
260                                 for (x = 0, ptr = tx->disp_data; x < tx->display_width; ptr++, x++) {
261                                         value = 0;
262                                         for (sample = x * tx->spp, avg_pos=1; sample < (x + 1) * tx->spp; sample++) {
263                                                 value += (abs(tx->data[sample])-value)/(double) avg_pos++;
264                                         }
265                                         if (value>tx->max_value) tx->max_value=value;
266                                         tx->disp_data[x] = value; 
267                                 }
268                                 for (x = 0; x < tx->display_width; x++) {
269                                         f_prec t=tx->disp_data[x]/(double) tx->max_value;
270                                         tx->disp_data[x]=(int) (t * (f_prec) (tx->yc));
271                                 }
272                         } else {
273 #endif                          
274                                 //puts("have max...");
275                                 /* We have a max value... */
276                                 for (x = 0, ptr = tx->disp_data; x < tx->display_width; ptr++, x++) {
277                                         f_prec t;
278                                         value = 0;
279                                         for (sample = x * tx->spp, avg_pos=1; sample < (x + 1) * tx->spp; sample++) {
280                                                 value += (abs(tx->data[sample])-value)/(double) avg_pos++;
281                                         }
282 #ifdef USE_DISPLAY_NORMALIZE                                    
283                                         t=value/(double) tx->max_value;
284 #else
285                                         t=value/32768.0;
286 #endif                                  
287                                         tx->disp_data[x] = (int) (t * (f_prec) (tx->yc)); 
288                                 }
289 #ifdef USE_DISPLAY_NORMALIZE
290                         }
291 #endif                  
292                 }
293         } else {
294             tx->disp_data = NULL;
295         }
296         
297         tx->cursor_pos=-1;
298         tx->lastmute=-1;
299         
300         //tX_warning("spp: %i samples: %i width %i x %i", tx->spp, tx->samples, tx->display_width, x);
301 }
302
303 static void gtk_tx_size_allocate(GtkWidget * widget, GtkAllocation * allocation) {
304         GtkTx *tx;
305
306         g_return_if_fail(widget != NULL);
307         g_return_if_fail(GTK_IS_TX(widget));
308         g_return_if_fail(allocation != NULL);
309
310         widget->allocation = *allocation;
311
312         gtk_tx_prepare(widget);
313
314         if (GTK_WIDGET_REALIZED(widget)) {
315             tx = GTK_TX(widget);
316 #ifdef USE_DISPLAY_NORMALIZE            
317                 tx->max_value=-1;
318 #endif          
319             gdk_window_move_resize(widget->window, allocation->x, allocation->y, allocation->width, allocation->height);
320         }
321 }
322
323 #define min(a,b) ((a) < (b) ? (a) : (b))
324 #define max(a,b) ((a) > (b) ? (a) : (b))
325
326 static gint gtk_tx_expose(GtkWidget * widget, GdkEventExpose * event) {
327         GtkTx *tx;
328         gint x;
329         GdkRectangle *area;
330         
331         g_return_val_if_fail(widget != NULL, FALSE);
332         g_return_val_if_fail(GTK_IS_TX(widget), FALSE);
333         g_return_val_if_fail(event != NULL, FALSE);
334
335 /*      if (event->count > 0) { 
336                 fprintf(stderr, "Ignoring %i expose events.\n", event->count);
337                 return FALSE;
338         }*/
339
340         area=&event->area;
341
342         tx = GTK_TX(widget);
343
344         gdk_gc_set_foreground(widget->style->fg_gc[widget->state], tx->current_bg);
345
346         gdk_draw_rectangle(widget->window,
347                 widget->style->fg_gc[widget->state], 1, 
348                 area->x, area->y,
349                 area->width, area->height);
350
351         gdk_gc_set_foreground(widget->style->fg_gc[widget->state], tx->current_fg);
352
353         if (tx->disp_data) {
354                 int max_x=area->x+area->width;
355
356             for (x =area->x; x < max_x; x++) {
357                         gdk_draw_line(widget->window,
358                                 widget->style->fg_gc[widget->state], x,
359                                 tx->yc - tx->disp_data[tx->display_x_offset+x], x,
360                                 tx->yc + tx->disp_data[tx->display_x_offset+x]);
361             }
362         } else {
363             gdk_draw_line(widget->window, widget->style->fg_gc[widget->state],
364                          0, tx->yc, widget->allocation.width, tx->yc);
365         }
366
367         return FALSE;
368 }
369
370 void gtk_tx_set_zoom(GtkTx *tx, f_prec zoom) {
371         GtkWidget *wid=GTK_WIDGET(tx);
372         
373         tx->zoom=zoom;
374         gtk_tx_prepare(wid);
375         gtk_widget_queue_draw_area(wid, 0, 0, wid->allocation.width, wid->allocation.height);
376 }
377
378 static void gtk_tx_update(GtkTx * tx) {
379         g_return_if_fail(tx != NULL);
380         g_return_if_fail(GTK_IS_TX(tx));
381
382         gtk_widget_draw(GTK_WIDGET(tx), NULL);
383 }
384
385 void gtk_tx_update_pos_display(GtkTx * tx, int sample, int mute) {
386         GtkWidget *widget;
387         GdkWindow *window;
388         GdkGC *gc;
389
390         int x, y, yc, ymax, tmp;
391         int current_pos, current_pos_x, x_offset;
392         int force_draw=0;
393
394         /* Don't update if not required */
395
396         //current_x = sample / tx->spp + FR_SIZE;
397         current_pos = sample / tx->spp;
398         
399         if ((current_pos == tx->cursor_pos) && 
400                 (tx->lastmute == mute)) return;
401         tx->lastmute = mute;
402
403         /* speedup + easyness */
404
405         widget = GTK_WIDGET(tx);
406         window = widget->window;
407
408         gc = widget->style->fg_gc[widget->state];
409         yc = tx->yc;
410         ymax = widget->allocation.height-1;
411
412         /* clean up last pos */
413         
414         x = tx->cursor_x_pos;
415         
416         if (x >= 0) {
417             gdk_gc_set_foreground(gc, tx->current_bg);
418             gdk_draw_line(window, gc, x, 0, x, ymax);
419
420             gdk_gc_set_foreground(gc, tx->current_fg);
421             y = tx->disp_data[x+tx->display_x_offset];
422             gdk_draw_line(window, gc, x, yc + y, x, yc - y);
423         }
424         
425         /* compute new position */
426         if (tx->zoom==0) {
427                 current_pos_x=current_pos;
428                 x_offset=0;             
429         } else {                
430                 tmp=widget->allocation.width/2+1;
431                 
432                 if (current_pos>tmp) {
433                         x_offset=current_pos-tmp;
434                         
435                         if (x_offset+widget->allocation.width>=tx->display_width) {
436                                 x_offset=tx->display_width-widget->allocation.width;
437                         }
438                         
439                         current_pos_x=current_pos-x_offset;
440                 } else {
441                         x_offset=0;
442                         current_pos_x=current_pos;
443                 }
444                 
445                 if (x_offset!=tx->display_x_offset) {
446                         int x_move=tx->display_x_offset-x_offset;
447                         
448                         if (abs(x_move)<widget->allocation.width) {
449                                 gdk_window_scroll(window, x_move, 0);
450                         } else {
451                                 /* we've moved so far that there's nothing to keep from our current display */
452                                 force_draw=1;
453                         }
454                 }
455         }
456         
457         /* store current_pos */
458
459         tx->cursor_pos = current_pos;
460         tx->cursor_x_pos = current_pos_x;
461         tx->display_x_offset = x_offset;
462         
463         /* not drawing current pos - let expose() take care of this... */
464
465         x = current_pos_x;
466
467         if (mute) gdk_gc_set_foreground(gc, &tx->mute);
468         else gdk_gc_set_foreground(gc, &tx->busy);
469
470         gdk_draw_line(window, gc, x, 0, x, ymax);
471         
472         if (force_draw) {
473                 gtk_widget_queue_draw_area(widget, 0, 0, widget->allocation.width, widget->allocation.height);
474         }
475 }
476
477 void gtk_tx_cleanup_pos_display(GtkTx * tx) {
478         GtkWidget *widget;
479         GdkWindow *window;
480         GdkGC *gc;
481         int ymax, yc;
482
483         widget = GTK_WIDGET(tx);
484         window = widget->window;
485         gc = widget->style->fg_gc[widget->state];
486         yc = tx->yc;
487         ymax = widget->allocation.height - 1;
488
489         tx->display_x_offset=0;
490         tx->cursor_pos=-1;
491         tx->cursor_x_pos=-1;
492         tx->do_showframe=0;
493         //tx->current_fg=&tx->fg;
494         
495         gtk_widget_queue_draw(widget);
496 }
497
498 void gtk_tx_show_frame(GtkTx *tx, int show) {
499         if (show) {
500                 tx->current_fg=&tx->focus_fg;
501                 tx->current_bg=&tx->focus_bg;
502         } else {
503                 tx->current_fg=&tx->fg;
504                 tx->current_bg=&tx->bg;
505         }
506         
507         gtk_widget_queue_draw(GTK_WIDGET(tx));  
508 }
509
510 f_prec gtk_tx_get_zoom(GtkTx *tx) {
511         return tx->zoom;
512 }