Add Spectrograph sources and test files.
[dictix.git] / libdictix / dix-spectrograph.c
blobccc79916e2df819dbab4e07a975b004f9cb29be2
1 /**
2 * Dictix / DixSpectrograph - dix-spectrograph.c
4 * Copyright (C) Martin Blanchard 2011 <tinram@gmx.fr>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <glib.h>
29 #include <glib/gi18n.h>
30 #include <gtk/gtk.h>
32 #include "dix-spectrograph.h"
34 enum {
35 PROP_0,
36 PROP_ACTIVE,
37 PROP_BANDS
40 struct _DixSpectrographPrivate
42 gboolean active;
44 guint bands;
46 gfloat* magnitudes;
49 #define DIX_SPECTROGRAPH_GET_PRIVATE(obj) \
50 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), DIX_TYPE_SPECTROGRAPH, DixSpectrographPrivate))
52 #define DIX_SPECTROGRAPH_BANDS 8
53 #define DIX_SPECTROGRAPH_THRESHOLD -70.0f
54 #define DIX_SPECTROGRAPH_HEIGHT 35
55 #define DIX_SPECTROGRAPH_HEIGHT_D 35.0
56 #define DIX_SPECTROGRAPH_LED_WIDTH 8
57 #define DIX_SPECTROGRAPH_LED_WIDTH_D 8.0
58 #define DIX_SPECTROGRAPH_LED_HEIGHT 5
59 #define DIX_SPECTROGRAPH_LED_HEIGHT_D 5.0
60 #define DIX_SPECTROGRAPH_LED_PADDING 1
61 #define DIX_SPECTROGRAPH_LED_PADDING_D 1.0
63 G_DEFINE_TYPE (DixSpectrograph, dix_spectrograph, GTK_TYPE_WIDGET)
65 static guint dix_spectrograph_log_scale (gfloat db);
67 static void
68 dix_spectrograph_set_property (GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec)
70 DixSpectrograph* spec = (DixSpectrograph *) object;
71 DixSpectrographPrivate* priv = spec->priv;
72 gboolean active;
74 switch (prop_id) {
75 case PROP_ACTIVE: {
76 active = g_value_get_boolean (value);
78 if (priv->active != active) {
79 priv->active = active;
81 if (priv->active == TRUE) {
82 gtk_widget_set_state_flags (GTK_WIDGET (spec),
83 GTK_STATE_FLAG_ACTIVE, FALSE);
84 } else {
85 gtk_widget_unset_state_flags (GTK_WIDGET (spec),
86 GTK_STATE_FLAG_ACTIVE);
89 g_object_notify (G_OBJECT (spec), "active");
92 break;
93 } default: {
94 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
96 break;
102 static void
103 dix_spectrograph_get_property (GObject* object, guint prop_id, GValue* value, GParamSpec* pspec)
105 DixSpectrograph* spec = (DixSpectrograph *) object;
106 DixSpectrographPrivate* priv = spec->priv;
108 switch (prop_id) {
109 case PROP_ACTIVE: {
110 g_value_set_boolean (value, priv->active);
112 break;
113 } case PROP_BANDS: {
114 g_value_set_uint (value, priv->bands);
116 break;
117 } default: {
118 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
120 break;
125 static void
126 dix_spectrograph_finalize (GObject *object)
128 DixSpectrograph* spec = (DixSpectrograph *) object;
129 DixSpectrographPrivate* priv = spec->priv;
131 G_OBJECT_CLASS (dix_spectrograph_parent_class)->finalize (object);
134 static void
135 dix_spectrograph_realize (GtkWidget* widget)
137 GdkWindow *window = NULL;
138 GtkAllocation allocation;
139 GdkWindowAttr attributes;
140 gint attributes_mask;
142 gtk_widget_get_allocation (widget, &allocation);
144 gtk_widget_set_realized (widget, TRUE);
146 attributes.wclass = GDK_INPUT_OUTPUT;
147 attributes.window_type = GDK_WINDOW_CHILD;
148 attributes.x = allocation.x;
149 attributes.y = allocation.y;
150 attributes.width = allocation.width;
151 attributes.height = allocation.height;
152 attributes.visual = gtk_widget_get_visual (widget);
153 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
155 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
157 window = gdk_window_new (gtk_widget_get_parent_window (widget),
158 &attributes, attributes_mask);
159 gtk_widget_set_window (widget, window);
160 gdk_window_set_user_data (window, widget);
164 static void
165 dix_spectrograph_get_preferred_width (GtkWidget *widget, gint *minimal, gint *natural)
167 DixSpectrograph* spec = (DixSpectrograph*) widget;
168 DixSpectrographPrivate* priv = spec->priv;
169 gint width = 0;
171 width = (priv->bands * DIX_SPECTROGRAPH_LED_WIDTH)
172 + ((priv->bands - 1) * DIX_SPECTROGRAPH_LED_PADDING);
174 *minimal = *natural = width;
177 static void
178 dix_spectrograph_get_preferred_height (GtkWidget *widget, gint *minimal, gint *natural)
180 gint height = 0;
182 height = DIX_SPECTROGRAPH_HEIGHT;
184 *minimal = *natural = height;
187 static gboolean
188 dix_spectrograph_draw (GtkWidget *widget, cairo_t *cr)
190 DixSpectrograph* spec = (DixSpectrograph *) widget;
191 DixSpectrographPrivate* priv = spec->priv;
192 GtkStyleContext* context = NULL;
193 GtkStateFlags state;
194 gint height;
195 guint level;
196 gint i, j;
198 context = gtk_widget_get_style_context (widget);
199 state = gtk_widget_get_state_flags (widget);
200 if (G_LIKELY (context != NULL)) {
201 gtk_style_context_set_state (context, state);
204 height = gtk_widget_get_allocated_height (widget);
206 for (i = 0; i < priv->bands; i++) {
207 level = dix_spectrograph_log_scale (priv->magnitudes[i]);
208 if (G_UNLIKELY (level > 6)) {
209 level = 6;
212 if (level == 0) {
213 cairo_rectangle (cr,
214 (gdouble) i * DIX_SPECTROGRAPH_LED_WIDTH_D
215 + (gdouble) i * DIX_SPECTROGRAPH_LED_PADDING_D,
216 (gdouble) height - 1.0,
217 DIX_SPECTROGRAPH_LED_WIDTH_D,
218 1.0);
219 cairo_set_source_rgb (cr, 0.00, 0.00, 0.00);
221 cairo_fill (cr);
222 } else {
223 for (j = level; j > 0 ; j--) {
224 cairo_rectangle (cr,
225 (gdouble) i * DIX_SPECTROGRAPH_LED_WIDTH_D
226 + (gdouble) i * DIX_SPECTROGRAPH_LED_PADDING_D,
227 (gdouble) height
228 - (gdouble) level * DIX_SPECTROGRAPH_LED_HEIGHT_D
229 - (gdouble) (level - 1) * DIX_SPECTROGRAPH_LED_PADDING_D,
230 DIX_SPECTROGRAPH_LED_WIDTH_D,
231 DIX_SPECTROGRAPH_LED_HEIGHT_D);
232 cairo_set_source_rgb (cr, 0.00, 0.00, 0.00);
234 cairo_fill (cr);
235 level--;
240 return FALSE;
243 static void
244 dix_spectrograph_class_init (DixSpectrographClass* klass)
246 GObjectClass* gobject_class = (GObjectClass *) klass;
247 GtkWidgetClass* widget_class = (GtkWidgetClass *) klass;
249 gobject_class->set_property = dix_spectrograph_set_property;
250 gobject_class->get_property = dix_spectrograph_get_property;
251 gobject_class->finalize = dix_spectrograph_finalize;
253 widget_class->realize = dix_spectrograph_realize;
254 widget_class->get_preferred_width = dix_spectrograph_get_preferred_width;
255 widget_class->get_preferred_height = dix_spectrograph_get_preferred_height;
256 widget_class->draw = dix_spectrograph_draw;
258 g_object_class_install_property (gobject_class,
259 PROP_ACTIVE,
260 g_param_spec_boolean ("active",
261 "Active",
262 "Whether the spectrograph is active",
263 FALSE,
264 G_PARAM_READWRITE));
266 g_object_class_install_property (gobject_class,
267 PROP_BANDS,
268 g_param_spec_uint ("bands",
269 "Bands",
270 "Number of frequency bands.",
271 6, 12, DIX_SPECTROGRAPH_BANDS,
272 G_PARAM_READABLE));
274 g_type_class_add_private (gobject_class, sizeof (DixSpectrographPrivate));
277 static void
278 dix_spectrograph_init (DixSpectrograph* spec)
280 DixSpectrographPrivate* priv = DIX_SPECTROGRAPH_GET_PRIVATE (spec);
281 spec->priv = priv;
282 gint i;
284 priv->active = FALSE;
285 gtk_widget_unset_state_flags (GTK_WIDGET (spec),
286 GTK_STATE_FLAG_ACTIVE);
288 priv->bands = DIX_SPECTROGRAPH_BANDS;
290 priv->magnitudes = g_malloc (priv->bands * sizeof (gfloat));
291 for (i = 0; i < priv->bands; i++) {
292 priv->magnitudes[i] = DIX_SPECTROGRAPH_THRESHOLD;
294 /*priv->magnitudes[0] = -70.0f;*/
295 /*priv->magnitudes[1] = -40.0f;*/
296 /*priv->magnitudes[2] = -30.0f;*/
297 /*priv->magnitudes[3] = -20.0f;*/
298 /*priv->magnitudes[4] = -15.0f;*/
299 /*priv->magnitudes[5] = -3.0f;*/
300 /*priv->magnitudes[6] = -1.0f;*/
301 /*priv->magnitudes[7] = -80.0f;*/
304 GtkWidget*
305 dix_spectrograph_new ()
307 return GTK_WIDGET (g_object_new (DIX_TYPE_SPECTROGRAPH, NULL));
310 void
311 dix_spectrograph_set_magnitudes (DixSpectrograph* spec, guint bands, gfloat* magnitudes)
313 g_return_if_fail (DIX_IS_SPECTROGRAPH (spec));
314 g_return_if_fail (bands == DIX_SPECTROGRAPH_BANDS);
315 g_return_if_fail (magnitudes != NULL);
316 DixSpectrographPrivate* priv = spec->priv;
317 gint i;
319 if (G_UNLIKELY (bands != priv->bands)) {
320 return;
323 for (i = 0; i < priv->bands; i++) {
324 if (G_UNLIKELY (magnitudes[i] == priv->magnitudes[i])) {
325 printf ("Improbable !\n");
326 continue;
329 if (magnitudes[i] < DIX_SPECTROGRAPH_THRESHOLD) {
330 priv->magnitudes[i] = DIX_SPECTROGRAPH_THRESHOLD;
331 } else if (magnitudes[i] > priv->magnitudes[i]) {
332 if (magnitudes[i] > 6.0f) {
333 priv->magnitudes[i] = 6.0f;
334 } else {
335 priv->magnitudes[i] = magnitudes[i];
337 } else if (magnitudes[i] < priv->magnitudes[i]) {
338 priv->magnitudes[i] -= 0.75f; /* Ballistic coefficient */
342 gtk_widget_queue_draw (GTK_WIDGET (spec));
345 static guint
346 dix_spectrograph_log_scale (gfloat db)
348 guint level = 0;
350 if (db <= -70.0) {
351 level = 0;
352 } else if (db < -37.2f) {
353 level = 1;
354 } else if (db < -25.8f) {
355 level = 2;
356 } else if (db < -17.0f) {
357 level = 3;
358 } else if (db < -9.4f) {
359 level = 4;
360 } else if (db < -1.7f) {
361 level = 5;
362 } else {
363 level = 6;
366 return level;