2 terminatorX - realtime audio scratching software
3 Copyright (C) 1999-2003 Alexander König
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.
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.
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.
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.
28 #include <gtk/gtkwindow.h>
29 #include "tX_widget.h"
31 #include "tX_global.h"
39 #define TX_DEFAULT_SIZE_X 100
40 #define TX_DEFAULT_SIZE_Y 30
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);
56 static GtkWidgetClass *parent_class = NULL;
58 /* widget "methods" */
60 guint gtk_tx_get_type() {
61 static guint tx_type = 0;
64 GtkTypeInfo tx_info = {
68 (GtkClassInitFunc) gtk_tx_class_init,
69 (GtkObjectInitFunc) gtk_tx_init,
75 tx_type = gtk_type_unique(gtk_widget_get_type(), &tx_info);
81 static void gtk_tx_class_init(GtkTxClass * gclass) {
82 GtkObjectClass *object_class;
83 GtkWidgetClass *widget_class;
85 object_class = (GtkObjectClass *) gclass;
86 widget_class = (GtkWidgetClass *) gclass;
88 parent_class = gtk_type_class(gtk_widget_get_type());
90 object_class->destroy = gtk_tx_destroy;
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;
101 void gtk_tx_mk_col(GtkTx * tx, GdkColor * col, float r, float g, float b) {
104 col->red = (gint) (r * max);
105 col->green = (gint) (g * max);
106 col->blue = (gint) (b * max);
108 gdk_colormap_alloc_color(gtk_widget_get_colormap(GTK_WIDGET(tx)), col, 0, 1);
111 static void gtk_tx_init(GtkTx * tx) {
114 tx->disp_data = NULL;
117 tx->do_showframe = 0;
118 #ifdef USE_DISPLAY_NORMALIZE
122 priv = gdk_colormap_new(gtk_widget_get_visual(GTK_WIDGET(tx)), 6);
123 gtk_widget_set_colormap(GTK_WIDGET(tx), priv);
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);
130 gtk_tx_mk_col(tx, &tx->busy, 1, 0.4, 0.4);
131 gtk_tx_mk_col(tx, &tx->mute, 1, 1, 1);
133 tx->current_fg=&tx->fg;
134 tx->current_bg=&tx->bg;
137 GtkWidget *gtk_tx_new(int16_t * wavdata, int wavsamples) {
140 tx = gtk_type_new(gtk_tx_get_type());
143 tx->samples = wavsamples;
145 tx->display_x_offset=0;
147 return GTK_WIDGET(tx);
150 static void gtk_tx_destroy(GtkObject * object) {
151 g_return_if_fail(object != NULL);
152 g_return_if_fail(GTK_IS_TX(object));
154 if (GTK_OBJECT_CLASS(parent_class)->destroy) {
155 (*GTK_OBJECT_CLASS(parent_class)->destroy) (object);
159 #define MAX_ZOOM_WIDTH 500000.0
161 void gtk_tx_set_data(GtkTx * tx, int16_t * wavdata, int wavsamples) {
162 g_return_if_fail(tx != NULL);
163 g_return_if_fail(GTK_IS_TX(tx));
166 tx->samples = wavsamples;
167 #ifdef USE_DISPLAY_NORMALIZE
171 gtk_tx_prepare(GTK_WIDGET(tx));
175 static void gtk_tx_realize(GtkWidget * widget) {
177 GdkWindowAttr attributes;
178 gint attributes_mask;
180 g_return_if_fail(widget != NULL);
181 g_return_if_fail(GTK_IS_TX(widget));
183 GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
186 attributes.x = widget->allocation.x;
187 attributes.y = widget->allocation.y;
188 attributes.width = widget->allocation.width;
189 attributes.height = widget->allocation.height;
190 attributes.wclass = GDK_INPUT_OUTPUT;
191 attributes.window_type = GDK_WINDOW_CHILD;
192 attributes.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK;
193 attributes.visual = gtk_widget_get_visual(widget);
194 attributes.colormap = gtk_widget_get_colormap(widget);
195 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
197 widget->window = gdk_window_new(widget->parent->window, &attributes, attributes_mask);
198 widget->style = gtk_style_attach(widget->style, widget->window);
200 gdk_window_set_user_data(widget->window, widget);
202 gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
205 static void gtk_tx_size_request(GtkWidget * widget, GtkRequisition * requisition) {
206 requisition->width = TX_DEFAULT_SIZE_X;
207 requisition->height = TX_DEFAULT_SIZE_Y;
210 static void gtk_tx_prepare(GtkWidget * widget) {
217 g_return_if_fail(widget != NULL);
218 g_return_if_fail(GTK_IS_TX(widget));
222 tx->yc = widget->allocation.height / 2;
224 if (tx->disp_data) free(tx->disp_data);
227 int max_spp=tx->samples/widget->allocation.width;
228 int min_spp=tx->samples/MAX_ZOOM_WIDTH;
231 if (min_spp==0) min_spp=1;
233 diff=max_spp-min_spp;
235 tx->spp=min_spp+diff*(1.0-tx->zoom);
236 tx->display_width = tx->samples/tx->spp;
238 #ifdef USE_DISPLAY_NORMALIZE
242 tx->disp_data = (int16_t *) malloc(tx->display_width * sizeof(int16_t));
245 #ifdef USE_DISPLAY_NORMALIZE
246 if (tx->max_value==-1) {
247 /* We haven't figured a max value yet... */
248 //puts("searching max...");
250 for (x = 0, ptr = tx->disp_data; x < tx->display_width; ptr++, x++) {
252 for (sample = x * tx->spp, avg_pos=1; sample < (x + 1) * tx->spp; sample++) {
253 value += (abs(tx->data[sample])-value)/(double) avg_pos++;
255 if (value>tx->max_value) tx->max_value=value;
256 tx->disp_data[x] = value;
258 for (x = 0; x < tx->display_width; x++) {
259 f_prec t=tx->disp_data[x]/(double) tx->max_value;
260 tx->disp_data[x]=(int) (t * (f_prec) (tx->yc));
264 //puts("have max...");
265 /* We have a max value... */
266 for (x = 0, ptr = tx->disp_data; x < tx->display_width; ptr++, x++) {
269 for (sample = x * tx->spp, avg_pos=1; sample < (x + 1) * tx->spp; sample++) {
270 value += (abs(tx->data[sample])-value)/(double) avg_pos++;
272 #ifdef USE_DISPLAY_NORMALIZE
273 t=value/(double) tx->max_value;
277 tx->disp_data[x] = (int) (t * (f_prec) (tx->yc));
279 #ifdef USE_DISPLAY_NORMALIZE
284 tx->disp_data = NULL;
286 //tX_warning("spp: %i samples: %i width %i x %i", tx->spp, tx->samples, tx->display_width, x);
289 static void gtk_tx_size_allocate(GtkWidget * widget, GtkAllocation * allocation) {
292 g_return_if_fail(widget != NULL);
293 g_return_if_fail(GTK_IS_TX(widget));
294 g_return_if_fail(allocation != NULL);
296 widget->allocation = *allocation;
298 gtk_tx_prepare(widget);
300 if (GTK_WIDGET_REALIZED(widget)) {
302 #ifdef USE_DISPLAY_NORMALIZE
305 gdk_window_move_resize(widget->window, allocation->x, allocation->y, allocation->width, allocation->height);
309 #define min(a,b) ((a) < (b) ? (a) : (b))
310 #define max(a,b) ((a) > (b) ? (a) : (b))
312 static gint gtk_tx_expose(GtkWidget * widget, GdkEventExpose * event) {
317 g_return_val_if_fail(widget != NULL, FALSE);
318 g_return_val_if_fail(GTK_IS_TX(widget), FALSE);
319 g_return_val_if_fail(event != NULL, FALSE);
321 /* if (event->count > 0) {
322 fprintf(stderr, "Ignoring %i expose events.\n", event->count);
330 gdk_gc_set_foreground(widget->style->fg_gc[widget->state], tx->current_bg);
332 gdk_draw_rectangle(widget->window,
333 widget->style->fg_gc[widget->state], 1,
335 area->width, area->height);
337 gdk_gc_set_foreground(widget->style->fg_gc[widget->state], tx->current_fg);
340 int max_x=area->x+area->width;
342 for (x =area->x; x < max_x; x++) {
343 gdk_draw_line(widget->window,
344 widget->style->fg_gc[widget->state], x,
345 tx->yc - tx->disp_data[tx->display_x_offset+x], x,
346 tx->yc + tx->disp_data[tx->display_x_offset+x]);
349 gdk_draw_line(widget->window, widget->style->fg_gc[widget->state],
350 0, tx->yc, widget->allocation.width, tx->yc);
356 void gtk_tx_set_zoom(GtkTx *tx, f_prec zoom) {
357 GtkWidget *wid=GTK_WIDGET(tx);
361 gtk_widget_queue_draw_area(wid, 0, 0, wid->allocation.width, wid->allocation.height);
364 static void gtk_tx_update(GtkTx * tx) {
365 g_return_if_fail(tx != NULL);
366 g_return_if_fail(GTK_IS_TX(tx));
368 gtk_widget_draw(GTK_WIDGET(tx), NULL);
371 void gtk_tx_prepare_pos_display(GtkTx * tx) {
376 void gtk_tx_update_pos_display(GtkTx * tx, int sample, int mute) {
381 int x, y, yc, ymax, tmp;
382 int current_pos, current_pos_x, x_offset;
385 /* Don't update if not required */
387 //current_x = sample / tx->spp + FR_SIZE;
388 current_pos = sample / tx->spp;
390 if ((current_pos == tx->cursor_pos) && (tx->lastmute == mute)) return;
393 /* speedup + easyness */
395 widget = GTK_WIDGET(tx);
396 window = widget->window;
398 gc = widget->style->fg_gc[widget->state];
400 ymax = widget->allocation.height-1;
402 /* clean up last pos */
404 x = tx->cursor_x_pos;
407 gdk_gc_set_foreground(gc, tx->current_bg);
408 gdk_draw_line(window, gc, x, 0, x, ymax);
410 gdk_gc_set_foreground(gc, tx->current_fg);
411 y = tx->disp_data[x+tx->display_x_offset];
412 gdk_draw_line(window, gc, x, yc + y, x, yc - y);
415 /* compute new position */
417 current_pos_x=current_pos;
420 tmp=widget->allocation.width/2+1;
422 if (current_pos>tmp) {
423 x_offset=current_pos-tmp;
425 if (x_offset+widget->allocation.width>=tx->display_width) {
426 x_offset=tx->display_width-widget->allocation.width;
429 current_pos_x=current_pos-x_offset;
432 current_pos_x=current_pos;
435 if (x_offset!=tx->display_x_offset) {
436 int x_move=tx->display_x_offset-x_offset;
438 if (abs(x_move)<widget->allocation.width) {
439 gdk_window_scroll(window, x_move, 0);
441 /* we've moved so far that there's nothing to keep from our current display */
447 /* store current_pos */
449 tx->cursor_pos = current_pos;
450 tx->cursor_x_pos = current_pos_x;
451 tx->display_x_offset = x_offset;
453 /* not drawing current pos - let expose() take care of this... */
457 if (mute) gdk_gc_set_foreground(gc, &tx->mute);
458 else gdk_gc_set_foreground(gc, &tx->busy);
460 gdk_draw_line(window, gc, x, 0, x, ymax);
463 gtk_widget_queue_draw_area(widget, 0, 0, widget->allocation.width, widget->allocation.height);
467 void gtk_tx_cleanup_pos_display(GtkTx * tx) {
473 widget = GTK_WIDGET(tx);
474 window = widget->window;
475 gc = widget->style->fg_gc[widget->state];
477 ymax = widget->allocation.height - 1;
479 tx->display_x_offset=0;
483 //tx->current_fg=&tx->fg;
485 gtk_widget_queue_draw(widget);
488 void gtk_tx_show_frame(GtkTx *tx, int show) {
490 tx->current_fg=&tx->focus_fg;
491 tx->current_bg=&tx->focus_bg;
493 tx->current_fg=&tx->fg;
494 tx->current_bg=&tx->bg;
497 gtk_widget_queue_draw(GTK_WIDGET(tx));
500 f_prec gtk_tx_get_zoom(GtkTx *tx) {