Verbose plugin loading, code and minor gui cleanups - Alex
[terminatorX.git] / src / tX_flash.c
1 /*
2     terminatorX - realtime audio scratching software
3     Copyright (C) 1999-2004  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_flash.c
20  
21     Description: This contains the implementation of the tx_flash_flash widget.
22 */    
23
24 #include <math.h>
25
26 #include <gtk/gtkwindow.h>
27 #define IS_TX_FLASH 1
28 #include "tX_flash.h"
29 #include "tX_types.h"
30 #include "tX_global.h"
31 #include <malloc.h>
32
33 #ifndef WIN32
34 #include <unistd.h>
35 #endif
36
37 #ifdef __cplusplus
38 extern "C" {
39 #endif /* __cplusplus */
40
41 #define MAX_VALUE 1.0f
42 // 32767
43 #define RED_BORDER 0.8f
44 // 25000
45 #define TX_FLASH_DEFAULT_SIZE_X 25
46 #define TX_FLASH_DEFAULT_SIZE_Y 30
47 #define DY 5
48 #define DX 5
49 #define DMINIX 2
50 #define S_MINIX 2
51 #define L_MINIX 3
52 #define DLEVEL 3
53 #define MAX_MAX_CYCLES 30;
54
55 /* pre dec */
56 static void gtk_tx_flash_class_init (GtkTxFlashClass *);
57 static void gtk_tx_flash_init (GtkTxFlash *tx_flash);
58 GtkWidget* gtk_tx_flash_new ();
59 static void gtk_tx_flash_destroy (GtkObject *object);
60 static void gtk_tx_flash_realize (GtkWidget *widget);
61 static void gtk_tx_flash_size_request (GtkWidget *widget, GtkRequisition *requisition);
62 static void gtk_tx_flash_size_allocate (GtkWidget *widget, GtkAllocation *allocation);
63 static gint gtk_tx_flash_expose (GtkWidget *widget, GdkEventExpose *event);
64
65 /* Local data */
66
67 static GtkWidgetClass *parent_class = NULL;
68
69 GType gtk_tx_flash_get_type ()
70 {
71         static GType tx_flash_type = 0;
72
73         if (!tx_flash_type) {
74                 static const GTypeInfo tx_flash_info = {
75                         sizeof (GtkTxFlashClass),
76                         NULL,
77                         NULL,
78                         (GClassInitFunc) gtk_tx_flash_class_init, 
79                         NULL,
80                         NULL,
81                         sizeof (GtkTxFlash),
82                 0,
83                         (GInstanceInitFunc) gtk_tx_flash_init,
84                 };
85
86                 tx_flash_type = g_type_register_static(GTK_TYPE_WIDGET, "GtkTxFlash", &tx_flash_info, 0);
87     }
88         
89         return tx_flash_type;
90 }
91
92 static void gtk_tx_flash_class_init (GtkTxFlashClass *gclass)
93 {
94         GtkObjectClass *object_class;
95         GtkWidgetClass *widget_class;
96         
97         object_class = (GtkObjectClass*) gclass;
98         widget_class = (GtkWidgetClass*) gclass;
99         
100         parent_class = gtk_type_class (gtk_widget_get_type ());
101         
102         object_class->destroy = gtk_tx_flash_destroy;
103         
104         widget_class->realize = gtk_tx_flash_realize;
105         widget_class->expose_event = gtk_tx_flash_expose;
106         widget_class->size_request = gtk_tx_flash_size_request;
107         widget_class->size_allocate = gtk_tx_flash_size_allocate;
108         //widget_class->button_press_event = gtk_tx_flash_button_press;
109         //widget_class->button_release_event = gtk_tx_flash_button_release;
110         //widget_class->motion_notify_event = gtk_tx_flash_motion_notify;
111 }
112
113 enum {
114         COL_BG,
115         COL_NORM,
116         COL_NORM_HALF,
117         COL_LOUD,
118         COL_LOUD_HALF,
119         NO_COLS
120 };
121
122 /* c=a+(a-b)*x; */
123
124 inline void mk_half(double s, GdkColor *a, GdkColor *b, GdkColor *c)
125 {
126         c->red=a->red-(a->red-b->red)*s;
127         c->green=a->green-(a->green-b->green)*s;
128         c->blue=a->blue-(a->blue-b->blue)*s;
129 }
130
131 void gtk_tx_flash_update_colors(GtkTxFlash *tx)
132 {
133         int i;
134         
135         if (tx->colors_allocated) {
136                 gdk_colormap_free_colors(gtk_widget_get_colormap(GTK_WIDGET(tx)), tx->colors, NO_COLS);
137         }
138         
139         gdk_color_parse(globals.vu_meter_bg, &tx->colors[COL_BG]);
140         gdk_color_parse(globals.vu_meter_loud, &tx->colors[COL_LOUD]);
141         gdk_color_parse(globals.vu_meter_normal, &tx->colors[COL_NORM]);        
142         mk_half(globals.vu_meter_border_intensity, &tx->colors[COL_BG], &tx->colors[COL_LOUD], &tx->colors[COL_LOUD_HALF]);
143         mk_half(globals.vu_meter_border_intensity, &tx->colors[COL_BG], &tx->colors[COL_NORM], &tx->colors[COL_NORM_HALF]);
144
145         for (i=0; i<NO_COLS; i++) {
146                 gdk_colormap_alloc_color(gtk_widget_get_colormap(GTK_WIDGET(tx)), &tx->colors[i], 0, 1);
147         }
148
149         tx->colors_allocated=1;
150 }
151
152 static void gtk_tx_flash_init (GtkTxFlash *tx_flash)
153 {
154         GdkColormap *priv;
155         
156         tx_flash->colors_allocated=0;
157         priv=gdk_colormap_new(gtk_widget_get_visual(GTK_WIDGET(tx_flash)), 6);
158
159         gtk_widget_set_colormap(GTK_WIDGET(tx_flash), priv);
160         gtk_tx_flash_update_colors(tx_flash);
161 }
162
163 GtkWidget* gtk_tx_flash_new ()
164 {
165         GtkTxFlash *tx_flash;
166         
167         tx_flash = (GtkTxFlash *)g_object_new(gtk_tx_flash_get_type (), NULL);  
168         return GTK_WIDGET (tx_flash);
169 }
170
171 static void gtk_tx_flash_destroy (GtkObject *object)
172 {
173         g_return_if_fail (object != NULL);
174         g_return_if_fail (GTK_IS_TX_FLASH (object));
175         
176         if (GTK_OBJECT_CLASS (parent_class)->destroy)
177                 (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
178 }
179
180 static void gtk_tx_flash_realize (GtkWidget *widget)
181 {
182         GtkTxFlash *tx_flash;
183         GdkWindowAttr attributes;
184         gint attributes_mask;
185         
186         g_return_if_fail (widget != NULL);
187         g_return_if_fail (GTK_IS_TX_FLASH (widget));
188         
189         GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
190         tx_flash = GTK_TX_FLASH (widget);
191         
192         attributes.x = widget->allocation.x;
193         attributes.y = widget->allocation.y;
194         attributes.width = widget->allocation.width;
195         attributes.height = widget->allocation.height;
196         attributes.wclass = GDK_INPUT_OUTPUT;
197         attributes.window_type = GDK_WINDOW_CHILD;
198         attributes.event_mask = gtk_widget_get_events (widget) | 
199                 GDK_EXPOSURE_MASK;
200         attributes.visual = gtk_widget_get_visual (widget);
201         attributes.colormap = gtk_widget_get_colormap (widget);
202         attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
203         
204         widget->window = gdk_window_new (widget->parent->window, &attributes, attributes_mask);
205         widget->style = gtk_style_attach (widget->style, widget->window);
206         
207         gdk_window_set_user_data (widget->window, widget);
208         gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
209 }
210
211 static void gtk_tx_flash_size_request(GtkWidget *widget, GtkRequisition *requisition)
212 {
213         requisition->width = TX_FLASH_DEFAULT_SIZE_X;
214         requisition->height = TX_FLASH_DEFAULT_SIZE_Y;
215 }
216
217 static void gtk_tx_flash_prepare(GtkWidget *widget)
218 {
219         GtkTxFlash *tx_flash;
220
221         g_return_if_fail (widget != NULL);
222         g_return_if_fail (GTK_IS_TX_FLASH (widget));
223         
224         tx_flash=GTK_TX_FLASH(widget);
225
226         tx_flash->levels=(widget->allocation.height-(2*DY))/DLEVEL;
227         tx_flash->channel[0].last_level=0;
228         tx_flash->channel[1].last_level=0;
229         tx_flash->channel[0].max=0;
230         tx_flash->channel[1].max=0;
231         tx_flash->level_value=MAX_VALUE/(f_prec) tx_flash->levels;
232         tx_flash->red_level=(RED_BORDER/tx_flash->level_value);
233         
234         tx_flash->channel[0].x1=DMINIX+S_MINIX+2;
235         tx_flash->channel[1].x2=widget->allocation.width-tx_flash->channel[0].x1-1;
236         
237         if (widget->allocation.width%2>0) {
238                 // odd
239                 tx_flash->center_expand=0;
240                 tx_flash->channel[0].x2=widget->allocation.width/2-2;
241         } else {
242                 // even
243                 tx_flash->center_expand=1;
244                 tx_flash->channel[0].x2=widget->allocation.width/2-3;
245         }
246         tx_flash->channel[1].x1=widget->allocation.width/2+2;
247         
248         //tX_msg("flash: width %i: left %i, right %i", widget->allocation.width, tx_flash->channel[0].x2-tx_flash->channel[0].x1, tx_flash->channel[1].x2-tx_flash->channel[1].x1);
249 }
250
251 static void gtk_tx_flash_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
252 {
253         GtkTxFlash *tx_flash;
254         
255         g_return_if_fail (widget != NULL);
256         g_return_if_fail (GTK_IS_TX_FLASH (widget));
257         g_return_if_fail (allocation != NULL);
258         
259         widget->allocation = *allocation;
260         
261         gtk_tx_flash_prepare(widget);
262         
263         if (GTK_WIDGET_REALIZED (widget)) {
264                 tx_flash = GTK_TX_FLASH (widget);
265                 
266                 gdk_window_move_resize (widget->window,
267                                   allocation->x, allocation->y,
268                                   allocation->width, allocation->height);
269         }
270 }
271
272 static void gtk_tx_flash_paint_yourself(GtkWidget *widget)
273 {
274         GtkTxFlash *tx_flash;
275         gint i, x11, x12,x21, x22, y, middle;
276         int long_level;
277         
278         tx_flash = GTK_TX_FLASH (widget);
279         
280         gdk_gc_set_foreground(widget->style->fg_gc[widget->state], &tx_flash->colors[COL_BG]);
281         
282         gdk_draw_rectangle(widget->window, widget->style->fg_gc[widget->state], 1, 0, 0, widget->allocation.width,widget->allocation.height); 
283         
284         gdk_gc_set_foreground(widget->style->fg_gc[widget->state], &tx_flash->colors[COL_NORM_HALF]);
285         
286         x12=DMINIX+S_MINIX;
287         x21=widget->allocation.width-1-x12;
288         middle=widget->allocation.width/2;
289         
290         for (i=0, y=widget->allocation.height-DY; i<=tx_flash->levels; y-=DLEVEL, i++) {
291                 if (i==0) {
292                         long_level=1;
293                 } else if (i==tx_flash->red_level-1) {
294                         long_level=1;
295                 } else if (i==tx_flash->red_level) {
296                         long_level=1;
297                         gdk_gc_set_foreground(widget->style->fg_gc[widget->state], &tx_flash->colors[COL_LOUD_HALF]);
298                 } else if (i==tx_flash->levels) {
299                         long_level=1;
300                 } else long_level=0;
301                 
302                 if (long_level) {
303                         x11=x12-L_MINIX;
304                         x22=x21+L_MINIX;
305                 } else {
306                         x11=x12-S_MINIX;
307                         x22=x21+S_MINIX;                
308                 }
309                 
310                 gdk_draw_line(widget->window, widget->style->fg_gc[widget->state], x11, y, x12, y);
311                 gdk_draw_line(widget->window, widget->style->fg_gc[widget->state], x21, y, x22, y);
312                 
313                 if (tx_flash->center_expand) {
314                         gdk_draw_line(widget->window, widget->style->fg_gc[widget->state], middle-1, y, middle, y);                     
315                 } else {
316                         gdk_draw_point(widget->window, widget->style->fg_gc[widget->state], middle, y);
317                 }
318         }
319 }
320
321 static gint gtk_tx_flash_expose (GtkWidget *widget, GdkEventExpose *event)
322 {
323         g_return_val_if_fail (widget != NULL, FALSE);
324         g_return_val_if_fail (GTK_IS_TX_FLASH (widget), FALSE);
325         g_return_val_if_fail (event != NULL, FALSE);
326         
327         if (event->count > 0)
328                 return FALSE;
329         
330         gtk_tx_flash_prepare(widget);
331         gtk_tx_flash_paint_yourself(widget);  
332         
333         return FALSE;
334
335
336
337 static void gtk_tx_flash_set_channel_level(GtkTxFlash *tx_flash, f_prec new_value, struct flash_channel *channel);
338
339 void gtk_tx_flash_set_level(GtkWidget *widget, f_prec left_channel, f_prec right_channel)
340 {
341         GtkTxFlash *tx_flash;
342         gint i, y;
343         int new_level;
344         int red=0;
345         
346         g_return_if_fail (widget != NULL);
347         g_return_if_fail (GTK_IS_TX_FLASH (widget));
348         
349         tx_flash = GTK_TX_FLASH (widget);
350         //printf("flash %08x: setting levels: %f, %f\n", tx_flash, left_channel, right_channel);        
351         
352         gtk_tx_flash_set_channel_level(tx_flash, left_channel, &tx_flash->channel[0]);
353         gtk_tx_flash_set_channel_level(tx_flash, right_channel, &tx_flash->channel[1]);
354 }
355
356 static void gtk_tx_flash_set_channel_level(GtkTxFlash *tx_flash, f_prec new_value, struct flash_channel *channel)
357 {
358         GtkWidget *widget=GTK_WIDGET(tx_flash);
359         gint i, y;
360         int new_level;
361         int red=0;
362         
363         new_level=(int) (new_value/tx_flash->level_value);
364         
365         if (new_level>tx_flash->levels) 
366                 new_level=tx_flash->levels;
367         
368         if (new_level>channel->max) {
369                 channel->max=new_level;
370                 tx_flash->max_cycles=MAX_MAX_CYCLES;
371         } else {
372                 tx_flash->max_cycles--;
373         }
374         
375         if (tx_flash->max_cycles <= 0) {
376                 y=widget->allocation.height-(DY+(channel->max)*DLEVEL);
377                 gdk_gc_set_foreground(widget->style->fg_gc[widget->state], &tx_flash->colors[COL_BG]);
378                 gdk_draw_line(widget->window, widget->style->fg_gc[widget->state], channel->x1, y, channel->x2, y);
379                 
380                 if (channel->max>0) {
381                         channel->max--;
382                         y+=DLEVEL;
383                         if (channel->max>tx_flash->red_level) {
384                                 gdk_gc_set_foreground(widget->style->fg_gc[widget->state], &tx_flash->colors[COL_LOUD]);
385                         } else {
386                                 gdk_gc_set_foreground(widget->style->fg_gc[widget->state], &tx_flash->colors[COL_NORM]);
387                         }
388                         
389                         gdk_draw_line(widget->window, widget->style->fg_gc[widget->state], channel->x1, y, channel->x2, y);
390                 }
391         }
392         
393         if (new_level==channel->last_level) 
394                 return;
395         
396         if (new_level<channel->last_level) {
397                 new_level=channel->last_level*globals.flash_response;
398         }
399         
400         if (new_level>channel->last_level) {
401                 gdk_gc_set_foreground(widget->style->fg_gc[widget->state], &tx_flash->colors[COL_NORM]);
402                 
403                 for (i=channel->last_level, y=widget->allocation.height-(DY+channel->last_level*DLEVEL); i<=new_level; y-=DLEVEL, i++) {
404                         if (!red) {
405                                 if (i>=tx_flash->red_level) {
406                                         gdk_gc_set_foreground(widget->style->fg_gc[widget->state], &tx_flash->colors[COL_LOUD]);
407                                         red=1;
408                                 }
409                         }
410                         gdk_draw_line(widget->window, widget->style->fg_gc[widget->state], channel->x1, y, channel->x2, y);
411                 }
412         } else {
413                 gdk_gc_set_foreground(widget->style->fg_gc[widget->state], &tx_flash->colors[COL_BG]);
414                 
415                 if (channel->last_level==channel->max) {
416                         i=channel->last_level-1;
417                 } else {
418                         i=channel->last_level;
419                 }
420                 
421                 for (y=widget->allocation.height-(DY+i*DLEVEL); i>new_level; y+=DLEVEL, i--) {
422                         gdk_draw_line(widget->window, widget->style->fg_gc[widget->state], channel->x1, y, channel->x2, y);
423                 }
424         }
425         channel->last_level=new_level;
426 }
427
428 void
429 gtk_tx_flash_clear (GtkWidget *widget)
430 {
431         GtkTxFlash *tx_flash;
432
433         tx_flash = GTK_TX_FLASH (widget);
434         
435         tx_flash->channel[0].max=0;
436         tx_flash->channel[1].max=0;
437         tx_flash->channel[0].last_level=0;
438         tx_flash->channel[1].last_level=0;
439         tx_flash->max_cycles=0;
440         
441         gtk_tx_flash_paint_yourself(widget);
442 }
443
444 #ifdef __cplusplus
445 }
446 #endif /* __cplusplus */