35 Commits
0.1.8 ... 0.3.0

Author SHA1 Message Date
Tomasz Moń
eadaf198ba Update README. 2012-03-26 16:57:04 +02:00
Tomasz Moń
97547476e4 Add contributors to the about dialog. 2012-03-26 16:55:00 +02:00
Tim LaBerge
35e42dc653 Detect DigiTech devices on startup. If more than one device is found, ask user which one to use. 2012-03-26 16:17:03 +02:00
Tim LaBerge
ce296d2698 1) Require libxml-2 and build new file preset_xml.c
2) In effects.c, some EffectsValues are changed to better reflect the
   strings used in the XML.

3) New EffectSettings are added for use when writing out effects to XML.

4) Added an XmlLabel structure for mapping a value to a string. Generally,
   these are the union of the set of all values over the set of supported
   devices.

5) There's a large array of XmlSettings used to derive the translation of
   a preset to XML.

6) Add an enum for product_id and use it to set a filter for file suffix.

7) Minor cleanup in push_message() that suppresses spurious error messages.

8) Fixed a bad memset in read_data_thread().

9) In gdigi.h, a few changes in names to be consistent with XML.

   There are some new definitions of LFO and VSWITCH parameters that
   need to be computed.

10) In gui.c, fixed up a compiler warning.

    Added a map from device id to file type and code to set a default filter
    in the read dialog.

    Wired up the 'save to XML' callback and reorganized the menue entries to
    better distinguish between 'save to file' and 'store to device'.

   In preset.c, sort the params so that what we write as XML more closely
   matches what is generated on the windows side.
2012-03-26 22:08:29 +08:00
Tomasz Moń
fd0369dc7c use GtkGrid instead of GtkTable 2011-10-15 16:24:57 +02:00
John Hammen
2c921df204 add RP150 support 2011-10-14 10:43:47 +02:00
Tomasz Moń
ee2fcd181e port to GTK+ 3 2011-09-28 15:32:30 +02:00
Ahmed Toulan
2eb4e97810 detect DigiTech devices on startup 2011-09-05 20:45:35 +02:00
John Hammen
907a3d0ed9 add RP155 support 2011-08-29 09:16:25 +02:00
Tomasz Moń
5dc5f0652f update HACKING file 2011-01-27 11:55:48 +01:00
Tomasz Moń
5a44076e4f Added tag 0.2.0 for changeset 6864af80db93 2010-10-07 11:09:14 +02:00
Tomasz Moń
0bb0f6db71 update manpage 2010-10-07 11:08:59 +02:00
Tomasz Moń
dcf66adfa9 fix RP1000 support 2010-08-25 11:15:29 +02:00
Tomasz Moń
0886ba00c1 add support for GNX4 and GNX3k patches 2010-06-17 19:20:58 +02:00
Jonathan A. Tice
e12de97fab add RP1000 support 2010-04-24 15:07:18 +02:00
Miklos Aubert
59753a61dc add RP255 support 2010-03-04 18:54:58 +01:00
Tomasz Moń
ff0be8230e fix some rp500 values 2010-02-05 09:43:21 +01:00
Tomasz Moń
07cfd70941 display values correctly (offsets, correct steps, suffixes, labels) 2010-01-03 14:18:53 +01:00
Rafael Moreno
2523b27205 add rp355p to SupportedFileTypes 2009-09-20 20:06:27 +02:00
Tomasz Moń
530dd397c5 remove USB settings from effect list as they're not part of Preset 2009-09-20 19:57:39 +02:00
Rafael Moreno
158fd081a5 fix RP355 amp B settings 2009-09-04 17:49:11 +02:00
Tomasz Moń
a8bac07f68 correct manpage 2009-08-11 12:18:03 +02:00
Andreas Karajannis
87bb59f43a don't use uninitialized value 2009-08-11 12:17:28 +02:00
Andrew O. Shadoura
cdeb821deb added manpage 2009-08-10 19:56:56 +01:00
Andrew O. Shadoura
ce463b29d8 added simple .desktop file 2009-08-08 21:04:40 +01:00
Andrew O. Shadoura
159bca158e use LDADD instead of LDFLAGS and -Wl,--as-needed; added install target 2009-08-08 20:53:07 +01:00
Tomasz Moń
6ae750c4fb GFile requires gio.h 2009-07-31 14:10:21 +02:00
Rafael Moreno
5f21c62a2f fix RP355 support 2009-07-28 11:38:24 +02:00
Tomasz Moń
08b20b398f remove redundant GFile 2009-07-19 18:52:57 +02:00
Tomasz Moń
1be664e82f rename Pickup to Pickup/USB as this frame contains both pickup and USB settings 2009-07-19 18:32:48 +02:00
Tomasz Moń
3ab55054c1 add restore_backup_file() 2009-07-19 16:15:23 +02:00
Tomasz Moń
97b8c9fd6d add create_backup_file() 2009-07-19 14:03:55 +02:00
Tomasz Moń
211df27cf8 fix typos in comments 2009-06-29 20:03:54 +02:00
Rafael Moreno
dfe28ac81d add RP355 support 2009-06-29 20:02:50 +02:00
Tomasz Moń
3da458d370 Added tag 0.1.8 for changeset 5e6d6124b5f2 2009-06-22 16:36:52 +02:00
20 changed files with 9249 additions and 585 deletions

10
AUTHORS
View File

@@ -1 +1,11 @@
Tomasz Moń <desowin@gmail.com>
Stephen Rigler <riglersc@gmail.com>
Jaco Kroon <jaco@kroon.co.za>
Rafael Moreno <laocanfei@yahoo.com>
Andrew O. Shadoura <bugzilla@tut.by>
Andreas Karajannis <aakara13@googlemail.com>
Miklos Aubert <miklos.aubert@gmail.com>
Jonathan A. Tice <jonandtice@gmail.com>
John Hammen <jhammen@gmail.com>
Ahmed Toulan <thelinuxer@gmail.com>
Tim LaBerge <tlaberge@visi.com>

58
HACKING
View File

@@ -10,36 +10,50 @@ There seems to be three possible ways to figure that out.
(all numbers here are hex)
04 F0 00 00 04 10 00 5E 04 02 41 00 04 30 09 00 07 00 34 F7
To get SysEx command out of it, remove every 4th byte, so we have:
MIDI data is transferred over USB using 32-bit USB-MIDI Event Packets.
Byte 0 is Cable Number (high nibble) and Code Index Number (low nibble).
Bytes 1, 2 and 3 are data.
Every bulk transfer on USB MIDI endpoint contains atleast one such packet
(those are sent one after each other).
In case of Digitech devices, the Cable Number seems to be 0 (correct me,
if I'm wrong).
There are three different Code Index Numbers that are being used in this
particular case:
0x4 - SysEx starts or continues (Byte 1, 2 and 3 from this packet are
part of our MIDI message)
0x5 - SysEx ends with following single byte (we just need to take Byte 1
from this packet)
0x6 - SysEx ends with following two bytes (we just need to take Byte 1 and 2
from this packet)
0x7 - SysEx ends with following three bytes (we need to take Byte 1, 2 and 3
from this packet)
Unused bytes in USB-MIDI packets are supposed to be 0.
To get SysEx command out of it, apply above rules, so we have:
F0 00 00 10 00 5E 02 41 00 30 09 00 00 34 F7
SysEx message format seems to be formed like this:
magic bytes - F0 00 00 10 00 5E 02 41
status byte - in this example 00
SysEx start byte - F0
Manufacturer ID - 00 00 10
Device ID - 00
Family ID - 5E (RP)
Product ID - 02 (RP250)
Procedure - 41 (see MessageID in gdigi.h)
As MIDI messages must not contain bytes with MSB bit set, Digitech
devices use packing system.
First byte contains MSB bits from following 7 bytes (this scheme
continues as many times as needed) (see pack_data() and unpack_message() in
gdigi.c for details).
Assuming message has been unpacked, the meaning of next bytes:
ID - in this example 30 09
Position - in this example 00
Value - in this example 00 (can be more bytes long, see below)
Checksum - to calculate it, XOR all bytes, and then XOR it with 07
Checksum - to calculate it, XOR all bytes (of packed message)
Every message ends with F7
Status byte bits explained:
7th - ???
6th - if active add 80 to ID
5th - ???
4th - is active when value will start with length
In such case value can be something like:
01 12 or 02 12 34 or 03 12 34 56
and proper values are:
01 - 12
02 - 1234
03 - 123456
3rd - when 4th bit is also active, and first value byte is 01
then add 80 to value
2nd - when 4th bit is also active, and first value byte is 02
then add 80 to value
1st - when 4th bit is also active, and first value byte is 03
then add 80 to value
So for above example:
ID = 3009 (hex) = 12297 (decimal)
Position = 0

View File

@@ -1,9 +1,10 @@
CC = gcc
EXTRA_CFLAGS ?=
EXTRA_LDFLAGS ?=
CFLAGS := $(shell pkg-config --cflags glib-2.0 gio-2.0 gtk+-2.0) -Wall -g -ansi -std=c99 $(EXTRA_CFLAGS)
LDFLAGS := $(shell pkg-config --libs glib-2.0 gio-2.0 gtk+-2.0 gthread-2.0 alsa) -lexpat $(EXTRA_LDFLAGS)
OBJECTS = gdigi.o gui.o effects.o preset.o gtkknob.o
CFLAGS := $(shell pkg-config --cflags glib-2.0 gio-2.0 gtk+-3.0 libxml-2.0) -Wall -g -ansi -std=c99 $(EXTRA_CFLAGS)
LDFLAGS = $(EXTRA_LDFLAGS) -Wl,--as-needed
LDADD := $(shell pkg-config --libs glib-2.0 gio-2.0 gtk+-3.0 gthread-2.0 alsa libxml-2.0) -lexpat
OBJECTS = gdigi.o gui.o effects.o preset.o gtkknob.o preset_xml.o
DEPFILES = $(foreach m,$(OBJECTS:.o=),.$(m).m)
.PHONY : clean distclean all
@@ -16,19 +17,19 @@ DEPFILES = $(foreach m,$(OBJECTS:.o=),.$(m).m)
all: gdigi
gdigi: $(OBJECTS)
$(CC) $(LDFLAGS) -o $@ $+
knob.h: knob.png
gdk-pixbuf-csource --name=knob_pixbuf knob.png > $@
$(CC) $(LDFLAGS) -o $@ $+ $(LDADD)
clean:
rm -f *.o
rm -f knob.h
distclean : clean
rm -f .*.m
rm -f gdigi
install: gdigi
install gdigi $(DESTDIR)/usr/bin
install gdigi.desktop $(DESTDIR)/usr/share/applications/
NODEP_TARGETS := clean distclean
depinc := 1
ifneq (,$(filter $(NODEP_TARGETS),$(MAKECMDGOALS)))

2
README
View File

@@ -1,4 +1,4 @@
Requirments: alsa, gtk+, glib, expat
Requirments: alsa, gtk+, glib, expat, libxml-2
Getting started guide:
-to compile: make

1
TODO
View File

@@ -2,5 +2,4 @@
-handling presets (saving, exporting to xml patches)
-buildsystem (install knob.png to share dir, don't use inline knob pixbuf)
-various fixes to MIDI IN messages handling
-guess device port when user doesn't explicitly provide it (don't use hardcoded "hw:1,0,0")
-optimizations

2081
effects.c

File diff suppressed because it is too large Load Diff

View File

@@ -19,10 +19,31 @@
#include <glib.h>
typedef struct {
typedef enum {
VALUE_TYPE_PLAIN = 0, /**< value displayed directly */
VALUE_TYPE_SUFFIX = 1 << 0, /**< use suffix for displaying */
VALUE_TYPE_OFFSET = 1 << 1, /**< use value offset */
VALUE_TYPE_STEP = 1 << 2, /**< use value step different than 1 */
VALUE_TYPE_LABEL = 1 << 3, /**< use value labels (overrides any other option) */
VALUE_TYPE_EXTRA = 1 << 4, /**< use extra values */
VALUE_TYPE_DECIMAL= 1 << 5, /**< display decimal places */
VALUE_TYPE_NONE = 1 << 6, /**< no value displayed */
} ValueType;
typedef struct _EffectValues {
gdouble min; /**< Minumum value */
gdouble max; /**< Maximum value */
gint type; /**< value type bitmask (ValueType) */
GStrv labels; /**< value labels */
gint offset; /**< value offset */
gdouble step; /**< value step */
gchar *suffix; /**< value suffix */
struct _EffectValues *extra;
/**< additional value range,
use it when there're different range types */
gint decimal; /**< amount of decimal places to display */
} EffectValues;
typedef struct {
@@ -73,6 +94,18 @@ typedef struct {
PresetBank bank;
} Banks;
enum product_id {
RP150 = 1,
RP250 = 2,
GNX4 = 3,
GNX3000 = 4,
RP500 = 5,
RP1000 = 6,
RP155 = 7,
RP255 = 8,
RP355 = 9,
};
typedef struct {
gchar *name;
unsigned char family_id;
@@ -85,6 +118,8 @@ typedef struct {
ModifierGroup *modifier_linkable_list();
void modifier_group_free(ModifierGroup *modifier_group);
void get_values_info(EffectValues *values,
gdouble *min, gdouble *max, gboolean *custom);
gboolean get_device_info(unsigned char device_id, unsigned char family_id,
unsigned char product_id,
Device **device);

74
gdigi.1 Normal file
View File

@@ -0,0 +1,74 @@
.\" Hey, EMACS: -*- nroff -*-
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.TH GDIGI 1 "October 07, 2010"
.\" Please adjust this date whenever revising the manpage.
.\"
.\" Some roff macros, for reference:
.\" .nh disable hyphenation
.\" .hy enable hyphenation
.\" .ad l left justify
.\" .ad b justify to both left and right margins
.\" .nf disable filling
.\" .fi enable filling
.\" .br insert line break
.\" .sp <n> insert n+1 empty lines
.\" for manpage-specific macros, see man(7)
.SH NAME
gdigi \- utility to control DigiTech effect pedals
.SH SYNOPSIS
.B gdigi
.RI [OPTION...]
.SH DESCRIPTION
gdigi is a tool aimed to provide X-Edit functionality to Linux users
.PP
Supported devices:
.IP \(bu
RP250
.IP \(bu
RP255
.IP \(bu
RP355
.IP \(bu
RP500
.IP \(bu
RP1000
.IP \(bu
GNX3000
.IP \(bu
GNX4K
.PP
.SH OPTIONS
These programs follow the usual GNU command line syntax, with long
options starting with two dashes (`\-').
A summary of options is included below.
For a complete description, see the Info files.
.TP
.B \-?, \-\-help
Show summary of options.
.TP
.B \-\-help\-all
Show all help options.
.TP
.B \-\-help\-gtk
Show GTK+ options.
.TP
.B \-\-display=\fIDISPLAY\fR
X display to use.
.TP
.B \-d, \-\-device
MIDI device port to use.
.SH AUTHOR
gdigi was written by Tomasz Moń <desowin@gmail.com>.
.PP
Send comments, bug reports and patches to gdigi
mailinglist at Google Groups.
.PP
This manual page was written by Andrew O. Shadoura <bugzilla@tut.by>,
for the Debian project (but may be used by others).
.PP
Permission is granted to copy, distribute and/or modify this document under the
terms of the GNU General Public License as published by the Free Software
Foundation, under version 3 of the License.

412
gdigi.c
View File

@@ -16,6 +16,7 @@
#include <stdio.h>
#include <gtk/gtk.h>
#include <gio/gio.h>
#include <getopt.h>
#include <alsa/asoundlib.h>
#include <alloca.h>
@@ -24,16 +25,32 @@
static unsigned char device_id = 0x7F;
static unsigned char family_id = 0x7F;
static unsigned char product_id = 0x7F;
unsigned char product_id = 0x7F;
static snd_rawmidi_t *output = NULL;
static snd_rawmidi_t *input = NULL;
static char *device_port = "hw:1,0,0";
static char *device_port = NULL;
static GQueue *message_queue = NULL;
static GMutex *message_queue_mutex = NULL;
static GCond *message_queue_cond = NULL;
/**
* Registers an error quark for gdigi if necessary.
*
* \return error quark used for gdigi errors
**/
static GQuark gdigi_error_quark()
{
static GQuark quark = 0;
if (quark == 0) {
quark = g_quark_from_static_string("gdigi-error");
}
return quark;
}
/**
* \param array data to calculate checksum
* \param length data length
@@ -105,13 +122,11 @@ GString *pack_data(gchar *data, gint len)
gint i;
gint new_len;
unsigned char status;
gint offset;
gint status_byte;
new_len = len + (len/7);
packed = g_string_sized_new(new_len);
status = 0;
offset = -1;
status_byte = 0;
for (i=0; i<len; i++) {
@@ -159,20 +174,27 @@ static void unpack_message(GString *msg)
do {
offset += 8;
status = str[offset-1];
for (x=0; x<7; x++) {
for (x=0; x<7 && !stop; x++) {
if (offset+x >= msg->len) {
i++;
stop = TRUE;
break;
}
if (str[offset+x] == 0xF7) {
if (x == 0) {
str[i] = status;
i++;
}
str[i] = 0xF7;
i++;
stop = TRUE;
break;
}
str[i] = (((status << (x+1)) & 0x80) | str[x+offset]);
i++;
}
} while (!stop && (offset+x < msg->len));
} while (!stop);
g_string_truncate(msg, i);
}
@@ -243,10 +265,12 @@ void push_message(GString *msg)
GDK_THREADS_ENTER();
g_timeout_add(0, apply_current_preset_to_gui, NULL);
GDK_THREADS_LEAVE();
} else
} else {
g_message("%d %d moved to %d %d", str[9], str[10], str[11], str[12]);
}
break;
default:
g_message("Received unhandled device notification");
g_message("Received unhandled device notification 0x%x", str[11]);
}
g_string_free(msg, TRUE);
return;
@@ -278,7 +302,7 @@ gpointer read_data_thread(gboolean *stop)
/* SysEx messages can't contain bytes with 8th bit set.
memset our buffer to 0xFF, so if for some reason we'll get out of reply bounds, we'll catch it */
memset(buf, sizeof(buf), 0xFF);
memset(buf, '\0', sizeof(buf));
err = poll(pfds, npfds, 200);
if (err < 0 && errno == EINTR)
@@ -540,6 +564,68 @@ void setting_param_free(SettingParam *param)
g_slice_free(SettingParam, param);
}
/**
* Allocates memory for SettingGenetx.
*
* \return SettingGenetx which must be freed using setting_genetx_free.
**/
SettingGenetx *setting_genetx_new()
{
SettingGenetx *genetx = g_slice_new(SettingGenetx);
/* Older patches don't specify GeNetX version */
genetx->version = GENETX_VERSION_1;
genetx->type = GENETX_TYPE_NOT_SET;
genetx->channel = -1;
genetx->name = NULL;
genetx->data = NULL;
return genetx;
}
/**
* \param genetx SettingGenetx to be freed
*
* Frees all memory used by SettingGenetx.
**/
void setting_genetx_free(SettingGenetx *genetx)
{
g_free(genetx->name);
if (genetx->data != NULL) {
g_string_free(genetx->data, TRUE);
}
g_slice_free(SettingGenetx, genetx);
}
/**
* \param version GeNetX version
* \param type GeNetX type
*
* Retrieves SectionID for specified GeNetX version and type.
*
* \return SectionID specified by version and type, or -1 on error.
**/
SectionID get_genetx_section_id(gint version, gint type)
{
if (version == GENETX_VERSION_1) {
if (type == GENETX_TYPE_AMP) {
return SECTION_GENETX_AMP;
} else if (type == GENETX_TYPE_CABINET) {
return SECTION_GENETX_CABINET;
}
} else if (version == GENETX_VERSION_2) {
if (type == GENETX_TYPE_AMP) {
return SECTION_GENETX2_AMP;
} else if (type == GENETX_TYPE_CABINET) {
return SECTION_GENETX2_CABINET;
}
}
g_message("This version of gdigi don't know what to do with this "
"GeNetX version (%d) and type (%d)", version, type);
return -1;
}
/**
* \param id Parameter ID
* \param position Parameter position
@@ -558,6 +644,68 @@ void set_option(guint id, guint position, guint value)
g_string_free(msg, TRUE);
}
/**
* \param section data section ID
* \param bank section-specific bank number
* \param index index of the desired object within the bank
* \param name object name
* \param data GString containing object data
*
* Forms RECEIVE_OBJECT SysEx message then sends it to device.
**/
void send_object(SectionID section, guint bank, guint index,
gchar *name, GString *data)
{
GString *msg = g_string_new(NULL);
gint len = data->len;
g_string_append_printf(msg,
"%c%c%c%c%s%c%c%c",
section, bank,
((index & 0xFF00) >> 8), (index & 0xFF),
name, 0 /* NULL terminated string */,
((len & 0xFF00) >> 8), (len & 0xFF));
g_string_append_len(msg, data->str, data->len);
send_message(RECEIVE_OBJECT, msg->str, msg->len);
g_string_free(msg, TRUE);
}
/**
* \param params GList containing SettingParam
*
* Forms RECEIVE_PRESET_PARAMETERS SysEx message then sends it to device.
**/
void send_preset_parameters(GList *params)
{
GString *msg = g_string_sized_new(500);
GList *iter = params;
gint len = g_list_length(iter);
g_string_append_printf(msg, "%c%c",
((len & 0xFF00) >> 8),
(len & 0xFF));
while (iter) {
SettingParam *param = (SettingParam *) iter->data;
iter = iter->next;
g_string_append_printf(msg, "%c%c%c",
((param->id & 0xFF00) >> 8),
(param->id & 0xFF),
param->position);
append_value(msg, param->value);
};
send_message(RECEIVE_PRESET_PARAMETERS, msg->str, msg->len);
g_string_free(msg, TRUE);
}
/**
* \param bank preset bank
* \param x preset index
@@ -653,11 +801,13 @@ GStrv query_preset_names(gchar bank)
}
/**
* Queries current edit buffer.
* Reads multiple messages and puts them into GList.
*
* \return GList with preset SysEx messages, which must be freed using preset_list_free.
* \param id MessageID starting message sequence
*
* \return GList with SysEx messages, which must be freed using message_list_free.
**/
GList *get_current_preset()
GList *get_message_list(MessageID id)
{
GString *data = NULL;
GList *list = NULL;
@@ -665,15 +815,13 @@ GList *get_current_preset()
gboolean found = FALSE;
gboolean done = FALSE;
send_message(REQUEST_PRESET, "\x04\x00", 2);
g_mutex_lock(message_queue_mutex);
do {
len = g_queue_get_length(message_queue);
for (x = 0; x<len && (found == FALSE); x++) {
data = g_queue_peek_nth(message_queue, x);
if (get_message_id(data) == RECEIVE_PRESET_START) {
if (get_message_id(data) == id) {
found = TRUE;
g_queue_pop_nth(message_queue, x);
unpack_message(data);
@@ -686,11 +834,23 @@ GList *get_current_preset()
int i;
int amt;
for (i = 10; (i < data->len) && data->str[i]; i++);
amt = (unsigned char)data->str[i+2];
switch (id) {
case RECEIVE_PRESET_START:
for (i = 10; (i < data->len) && data->str[i]; i++);
amt = (unsigned char)data->str[i+2];
break;
case RECEIVE_BULK_DUMP_START:
amt = ((unsigned char)data->str[8] << 8) | (unsigned char)data->str[9];
break;
default:
g_error("get_message_list() doesn't support followning id: %d", id);
g_string_free(data, TRUE);
g_list_free(list);
return NULL;
}
while (amt) {
g_message("%d messages left", amt);
data = g_queue_pop_nth(message_queue, x);
if (data == NULL) {
g_cond_wait(message_queue_cond, message_queue_mutex);
@@ -703,7 +863,7 @@ GList *get_current_preset()
done = TRUE;
} else {
/* Receive Preset Start not found in message queue */
/* id not found in message queue */
g_cond_wait(message_queue_cond, message_queue_mutex);
}
} while (done == FALSE);
@@ -712,7 +872,12 @@ GList *get_current_preset()
return list;
}
void preset_list_free(GList *list)
/**
* \param list list to be freed
*
* Frees all memory used by message list.
**/
void message_list_free(GList *list)
{
g_return_if_fail(list != NULL);
@@ -720,6 +885,160 @@ void preset_list_free(GList *list)
g_list_free(list);
}
/**
* Queries current edit buffer.
*
* \return GList with preset SysEx messages, which must be freed using message_list_free.
**/
GList *get_current_preset()
{
send_message(REQUEST_PRESET, "\x04\x00", 2);
return get_message_list(RECEIVE_PRESET_START);
}
/**
* Creates backup file.
*
* \param file backup file handle
* \param error a GError
*
* \return FALSE on success, TRUE on error.
**/
static gboolean create_backup_file(GFile *file, GError **error)
{
GFileOutputStream *output = NULL;
GList *list = NULL, *iter = NULL;
const gchar header[] = {'\x01', '\x00'};
gsize written;
gboolean val;
if (error)
*error = NULL;
output = g_file_create(file, G_FILE_CREATE_NONE, NULL, error);
if (output == NULL)
return TRUE;
if (error)
*error = NULL;
val = g_output_stream_write_all(G_OUTPUT_STREAM(output), header,
sizeof(header), &written, NULL, error);
if (val == FALSE) {
g_object_unref(output);
return TRUE;
}
send_message(REQUEST_BULK_DUMP, "\x00", 1);
list = get_message_list(RECEIVE_BULK_DUMP_START);
for (iter = list; iter; iter = g_list_next(iter)) {
GString *str;
guchar id; /* message id */
guint32 len; /* message length */
str = (GString*) iter->data;
id = get_message_id(str);
if (error)
*error = NULL;
val = g_output_stream_write_all(G_OUTPUT_STREAM(output), &id,
sizeof(id), &written, NULL, error);
if (val == FALSE) {
message_list_free(list);
g_object_unref(output);
return TRUE;
}
len = GUINT32_TO_LE(str->len - 10);
if (error)
*error = NULL;
val = g_output_stream_write_all(G_OUTPUT_STREAM(output), &len,
sizeof(len), &written, NULL, error);
if (val == FALSE) {
message_list_free(list);
g_object_unref(output);
return TRUE;
}
if (error)
*error = NULL;
val = g_output_stream_write_all(G_OUTPUT_STREAM(output), &str->str[8],
str->len - 10, &written, NULL, error);
if (val == FALSE) {
message_list_free(list);
g_object_unref(output);
return TRUE;
}
}
message_list_free(list);
if (error)
*error = NULL;
val = g_output_stream_close(G_OUTPUT_STREAM(output), NULL, error);
g_object_unref(output);
return !val;
}
/**
* Restores backup file.
*
* \param filename backup filename
* \param error a GError
*
* \return FALSE on success, TRUE on error.
**/
static gboolean restore_backup_file(const gchar *filename, GError **error)
{
gchar *data;
gsize length;
gsize x;
if (g_file_get_contents(filename, &data, &length, error) == FALSE)
return TRUE;
if (error)
*error = NULL;
if (!(data[0] == 0x01 && data[1] == 0x00)) {
g_free(data);
g_set_error_literal(error, gdigi_error_quark(), 0,
"Magic byte doesn't match");
return TRUE;
}
x = 0x02;
while (x < length) {
gchar id;
guint32 len;
id = data[x];
x++;
if (x+4 <= length) {
len = GUINT32_FROM_LE(*((guint32*) &data[x]));
x += 4;
} else {
g_free(data);
g_set_error_literal(error, gdigi_error_quark(), 0,
"Unexpected end of data");
return TRUE;
}
if (x+len <= length) {
send_message(id, &data[x], len);
x += len;
} else {
g_free(data);
g_set_error_literal(error, gdigi_error_quark(), 0,
"Unexpected end of data");
return TRUE;
}
}
g_free(data);
return FALSE;
}
/**
* \param device_id Variable to hold device ID
* \param family_id Variable to hold family ID
@@ -739,6 +1058,8 @@ static gboolean request_who_am_i(unsigned char *device_id, unsigned char *family
*device_id = data->str[8];
*family_id = data->str[9];
*product_id = data->str[10];
g_message("I am device id %d family %d product id %d.",
*device_id, *family_id, *product_id);
g_string_free(data, TRUE);
return TRUE;
}
@@ -791,6 +1112,31 @@ static GOptionEntry options[] = {
#endif /* DOXYGEN_SHOULD_SKIP_THIS */
/**
* \param[out] devices GList containing numbers (packed into pointers)
* of connected DigiTech devices
*
* Checks available soundcards for DigiTech devices.
*
* \return the number of connected DigiTech devices.
**/
static gint get_digitech_devices(GList **devices)
{
gint card_num = -1;
gint number = 0;
while (!snd_card_next(&card_num) && (card_num > -1)) {
char* name;
snd_card_get_longname(card_num, &name);
if (strspn(name,"DigiTech") > 0) {
number++;
*devices = g_list_append(*devices, GINT_TO_POINTER(card_num));
}
}
return number;
}
int main(int argc, char *argv[]) {
GError *error = NULL;
GOptionContext *context;
@@ -811,6 +1157,32 @@ int main(int argc, char *argv[]) {
exit(EXIT_FAILURE);
}
if (device_port == NULL) {
/* port not given explicitly in commandline - search for devices */
GList *devices = NULL;
GList *device = NULL;
int num_devices = 0;
int chosen_device = 0;
if ((num_devices = get_digitech_devices(&devices)) <= 0) {
g_message("Couldn't find any DigiTech devices!");
exit(EXIT_FAILURE);
}
if (num_devices > 1) {
chosen_device = select_device_dialog(devices);
if (chosen_device < 0) {
show_error_message(NULL, "No device chosen");
exit(EXIT_FAILURE);
}
}
device = g_list_nth(devices, chosen_device);
device_port = g_strdup_printf("hw:%d,0,0",
GPOINTER_TO_INT(device->data));
g_list_free(devices);
g_message("Found device %s", device_port);
} else {
g_message("Using device %s", device_port);
}
g_option_context_free(context);
if (open_device() == TRUE) {

7
gdigi.desktop Normal file
View File

@@ -0,0 +1,7 @@
[Desktop Entry]
Version=1.0
Type=Application
Name=gdigi
Exec=gdigi
Categories=GNOME;Audio;AudioVideo;

340
gdigi.h
View File

@@ -29,6 +29,8 @@
#define GNX_CABINET_WARP 263
#define GNX_CHANNEL_FS_MODE 264
unsigned char product_id;
enum {
GNX3K_WAH_TYPE_CRY = 129,
GNX3K_WAH_TYPE_BOUTIQUE = 130,
@@ -48,6 +50,9 @@ enum {
#define WAH_ON_OFF 129
#define WAH_POSITION 3
#define WAH_PEDAL_POSITION 132
#define WAH_VOLUME_BOOST 133
#define WAH_LEVEL 133
enum {
@@ -93,8 +98,8 @@ enum {
#define COMP_OUTPUT 212
enum {
PICKUP_TYPE_SC_HB = 65,
PICKUP_TYPE_HB_SC = 66,
PICKUP_TYPE_SC_HB = 65
};
#define PICKUP_TYPE 64
@@ -104,10 +109,15 @@ enum {
enum {
DIST_TYPE_SCREAMER = 1280,
DIST_TYPE_808 = 1292,
DIST_TYPE_TS_MOD = 1301,
DIST_TYPE_SD_ODRV = 1302,
DIST_TYPE_OD_ODRV = 1299,
DIST_TYPE_SPARKDRIVE = 1286,
DIST_TYPE_GUYOD = 1285,
DIST_TYPE_DOD250 = 1283,
DIST_TYPE_REDLINE = 1297,
DIST_TYPE_AMPDRIVR = 1298,
DIST_TYPE_OC_DRIVE = 1300,
DIST_TYPE_RODENT = 1281,
DIST_TYPE_MX = 1291,
DIST_TYPE_DS = 1282,
@@ -134,6 +144,17 @@ enum {
#define DIST_808_TONE 2474
#define DIST_808_LVL 2475
#define DIST_TS_MOD_DRIVE 2562
#define DIST_TS_MOD_TONE 2564
#define DIST_TS_MOD_LVL 2563
#define DIST_SD_ODRV_DRIVE 2565
#define DIST_SD_ODRV_TONE 2566
#define DIST_SD_ODRV_LVL 2567
#define DIST_OD_ODRV_OVERDRIVE 2568
#define DIST_OD_ODRV_LVL 2569
#define DIST_SPARKDRIVE_GAIN 2450
#define DIST_SPARKDRIVE_TONE 2451
#define DIST_SPARKDRIVE_CLEAN 2452
@@ -150,12 +171,22 @@ enum {
#define DIST_REDLINE_HIGH 2490
#define DIST_REDLINE_LEVEL 2491
#define DIST_AMPDRIVR_GAIN 2570
#define DIST_AMPDRIVR_MIDBOOST 2571
#define DIST_AMPDRIVR_LVL 2572
#define DIST_OC_DRIVE_DRIVE 2492
#define DIST_OC_DRIVE_TONE 2494
#define DIST_OC_DRIVE_HP_LP 2493
#define DIST_OC_DRIVE_LVL 2495
#define DIST_RODENT_DIST 2437
#define DIST_RODENT_FILTER 2438
#define DIST_RODENT_LVL 2439
#define DIST_MX_DIST 2468
#define DIST_MX_OUTPUT 2469
#define DIST_DS_GAIN 2440
#define DIST_DS_TONE 2441
#define DIST_DS_LVL 2442
@@ -201,6 +232,9 @@ enum {
#define DIST_MP_TONE 2446
#define DIST_MP_VOLUME 2447
#define VOLUME_PRE_FX_POSITION 13
#define VOLUME_POST_FX_POSITION 17
#define PRESET_POSITION 18
#define PRESET_LEVEL 2626
@@ -213,7 +247,7 @@ enum {
AMP_TYPE_BLACKFACE_DELUXE = 312,
AMP_TYPE_PLEXI_JTM_45 = 313,
AMP_TYPE_SUPER_LEAD_PLEXI = 314,
AMP_TYPE_PLEXI_JUMP_PANEL = 315,
AMP_TYPE_68_MARSHALL_JUMP = 315,
AMP_TYPE_MASTER_VOLUME = 316,
AMP_TYPE_JCM800 = 317,
AMP_TYPE_JCM900 = 318,
@@ -225,8 +259,10 @@ enum {
AMP_TYPE_BOOGIE_MARK_IV = 371,
AMP_TYPE_DUAL_RECTIFIER = 321,
AMP_TYPE_TRIPLE_RECTIFIER = 370,
AMP_TYPE_22_CALIBR = 386,
AMP_TYPE_LEGACY_VL100 = 327,
AMP_TYPE_MATCHLESS_HC30 = 326,
AMP_TYPE_CHIEF = 385,
AMP_TYPE_SOLDANO_100 = 325,
AMP_TYPE_SUPERGROUP = 381,
AMP_TYPE_GA40 = 380,
@@ -239,7 +275,7 @@ enum {
AMP_TYPE_METAL = 332,
AMP_TYPE_BRIGHT = 333,
AMP_TYPE_CHUNK = 334,
AMP_TYPE_CLEAN = 335,
AMP_TYPE_DIGITECH_CLEAN = 335,
AMP_TYPE_HIGH_GAIN = 337,
AMP_TYPE_BLUES = 338,
AMP_TYPE_FUZZ = 339,
@@ -280,7 +316,20 @@ enum {
#define AMP_TYPE 2496
#define AMP_ON_OFF 265
#define AMP_POSITION 8
#define CH2_AMP_POSITION 10
#define AMP_B_POSITION 10
#define AMP_LOOP_ON_OFF 3649
#define AMP_LOOP_POSITION 33
#define AMP_BYPASS_ON_OFF 12361
#define AMP_BYPASS_POSITION 0
#define AMP_CHANNEL 256
#define AMP_CHANNEL_POSITION 7
#define AMP_CHANNEL_A 0
#define AMP_CHANNEL_B 1
#define AMP_CHANNEL_WARP 2
#define AMP_GAIN 2497
#define AMP_LEVEL 2498
@@ -299,7 +348,7 @@ enum {
#define AMP_CAB_TYPE 2560
#define AMP_CAB_TUNING 2561
#define AMP_CAB_POSITION 9
#define CH2_AMP_CAB_POSITION 11
#define AMP_CAB_B_POSITION 11
enum {
AMP_CAB_DIRECT = 570,
@@ -312,6 +361,7 @@ enum {
AMP_CAB_TWIN = 576,
AMP_CAB_BRITISH2_12 = 613,
AMP_CAB_JAZZ2_12 = 626,
AMP_CAB_JBL_215 = 627,
AMP_CAB_BASSMAN = 579,
AMP_CAB_BRITISH4_12 = 614,
AMP_CAB_BRITISH_GREEN = 616,
@@ -365,21 +415,24 @@ enum {
GNX_AMP_CAB_SVT_BASS8_10 = 595,
};
#define EQ_TYPE 3202
#define EQ_ON_OFF 3212
#define EQ_POSITION 24
#define EQ_A_POSITION 24
#define EQ_B_POSITION 25
#define EQ_TYPE 3202
#define EQ_BASS 3203
#define EQ_MID 3204
#define EQ_MID_HZ 3206
#define EQ_TREBLE 3205
#define EQ_TREBLE_HZ 3211
#define EQ_LOW_LEVEL 3203
#define EQ_MID_LEVEL 3204
#define EQ_HIGH_LEVEL 3205
#define EQ_TREB 3205
#define EQ_MID_FREQ 3206
#define EQ_PRESENCE 3207
#define EQ_TREB_FREQ 3211
#define EQ_ENABLE 3212
#define EQ_LOW_FREQ 3213
#define EQ_MID_FREQ 3214
#define EQ_MID_FREQ_XXX 3214
#define EQ_HIGH_FREQ 3215
#define EQ_TREBLE_FREQ 3215
#define EQ_LOW_BANDWIDTH 3216
#define EQ_MID_BANDWIDTH 3217
#define EQ_HIGH_BANDWIDTH 3218
@@ -424,46 +477,57 @@ enum {
};
enum {
CHORUS_TYPE_CE = 0x37B,
CHORUS_TYPE_TC = 0x37C,
CHORUS_TYPE_DUAL = 0x379,
CHORUS_TYPE_GLISTEN = 0x392,
CHORUS_TYPE_MULTI = 0x37a,
CHORUS_TYPE_FLANGER = 0x37d,
CHORUS_TYPE_TRIGGERED_FLANGER = 0x37e,
CHORUS_TYPE_MXR_FLANGER = 0x37f,
CHORUS_TYPE_EH_FLANGER = 0x380,
CHORUS_TYPE_AD_FLANGER = 0x393,
CHORUS_TYPE_PHASER = 0x381,
CHORUS_TYPE_TRIGGERED_PHASER = 0x382,
CHORUS_TYPE_MX_PHASER = 0x383,
CHORUS_TYPE_EH_PHASER = 0x384,
CHORUS_TYPE_VIBRATO = 0x360,
CHORUS_TYPE_ROTARY = 0x361,
CHORUS_TYPE_VIBROPAN = 0x38f,
CHORUS_TYPE_UNOVIBE = 0x390,
CHORUS_TYPE_TREMOLO = 0x35e,
CHORUS_TYPE_SCATTER_TREM = 0x394,
CHORUS_TYPE_OPTO_TREMOLO = 0x388,
CHORUS_TYPE_BIAS_TREMOLO = 0x389,
CHORUS_TYPE_PANNER = 0x35f,
CHORUS_TYPE_ENVELOPE = 0x38a,
CHORUS_TYPE_FX25 = 0x38e,
CHORUS_TYPE_AUTOYA = 0x38b,
CHORUS_TYPE_YAYA = 0x38c,
CHORUS_TYPE_SYNTH_TALK = 0x391,
CHORUS_TYPE_STEP_FILTER = 0x38d,
CHORUS_TYPE_SAMPLE_HOLD = 0x395,
CHORUS_TYPE_WHAMMY = 0x540,
CHORUS_TYPE_PITCH_SHIFT = 0x543,
CHORUS_TYPE_DETUNE = 0x542,
CHORUS_TYPE_IPS = 0x541,
CHORUS_TYPE_OCTAVER = 0x385,
CHORUS_TYPE_CE = 891,
CHORUS_TYPE_TC = 892,
CHORUS_TYPE_DUAL = 889,
CHORUS_TYPE_GLISTEN = 914,
CHORUS_TYPE_MULTI = 890,
CHORUS_TYPE_VOO_DOO = 918,
CHORUS_TYPE_CLONE = 919,
CHORUS_TYPE_FLANGER = 893,
CHORUS_TYPE_TRIGGERED_FLANGER = 894,
CHORUS_TYPE_FLTFLANGER = 920,
CHORUS_TYPE_MXR_FLANGER = 895,
CHORUS_TYPE_EH_FLANGER = 896,
CHORUS_TYPE_AD_FLANGER = 915,
CHORUS_TYPE_PHASER = 897,
CHORUS_TYPE_TRIGGERED_PHASER = 898,
CHORUS_TYPE_MX_PHASER = 899,
CHORUS_TYPE_EH_PHASER = 900,
CHORUS_TYPE_VIBRATO = 864,
CHORUS_TYPE_ROTARY = 865,
CHORUS_TYPE_VIBROPAN = 911,
CHORUS_TYPE_UNOVIBE = 912,
CHORUS_TYPE_TREMOLO = 862,
CHORUS_TYPE_SCATTER_TREM = 916,
CHORUS_TYPE_OPTO_TREMOLO = 904,
CHORUS_TYPE_BIAS_TREMOLO = 905,
CHORUS_TYPE_PANNER = 863,
CHORUS_TYPE_ENVELOPE = 906,
CHORUS_TYPE_FX25 = 910,
CHORUS_TYPE_AUTOYA = 907,
CHORUS_TYPE_YAYA = 908,
CHORUS_TYPE_SYNTH_TALK = 913,
CHORUS_TYPE_STEP_FILTER = 909,
CHORUS_TYPE_SAMPLE_HOLD = 917,
CHORUS_TYPE_WHAMMY = 1344,
CHORUS_TYPE_PITCH_SHIFT = 1347,
CHORUS_TYPE_DETUNE = 1346,
CHORUS_TYPE_IPS = 1345, // Also known as Harmony
CHORUS_TYPE_OCTAVER = 901,
};
#define MOD_TYPE 768
#define MOD_POSITION 768
#define MOD_PRE_POST 1798
#define CHORUSFX_TYPE 768
#define CHORUSFX_ON_OFF 769
#define CHORUSFX_PRE_POST 770
#define CHORUSFX_POSITION 14
#define CHORUSFX_PRE 1543
#define CHORUSFX_POST 1544
#define CHORUS_SPEED 837
#define CHORUS_DEPTH 838
@@ -472,10 +536,18 @@ enum {
#define CHORUS_WIDTH 848
#define CHORUS_INTENSITY 849
enum {
CHORUS_WAVEFORM_TRI = 0,
CHORUS_WAVEFORM_SINE = 1,
CHORUS_WAVEFORM_SQUARE = 2,
};
#define CHORUS_LEVEL 836
#define CHORUS_WAVE 840
#define CHORUS_BALANCE 841
#define CHORUS_RATE 850
#define FLANGER_SPEED 902
#define FLANGER_DEPTH 903
#define FLANGER_REGEN 904
@@ -489,6 +561,8 @@ enum {
#define TRIG_FLANGER_MIX 1029
#define TRIG_FLANGER_LEVEL 1032
#define FLTFLANGER_FREQ 922
#define MXR_FLANGER_WIDTH 914
#define MXR_FLANGER_MANUAL 917
@@ -585,6 +659,25 @@ enum {
#define WHAMMY_PEDAL 1795
#define WHAMMY_MIX 1796
enum whammy_amt {
WHAMMY_TYPE_OCT_UP = 0,
WHAMMY_TYPE_2OCT_UP = 1,
WHAMMY_TYPE_2ND_DN = 2,
WHAMMY_TYPE_RV2ND_DN = 3,
WHAMMY_TYPE_4TH_DN = 4,
WHAMMY_TYPE_OCT_DN = 5,
WHAMMY_TYPE_2OCT_DN = 6,
WHAMMY_TYPE_DIV_BMB = 7,
WHAMMY_TYPE_M3_GT_MA3 = 8,
WHAMMY_TYPE_2ND_MA3 = 9,
WHAMMY_TYPE_3RD_4TH = 10,
WHAMMY_TYPE_4TH_5TH = 11,
WHAMMY_TYPE_5TH_OCT = 12,
WHAMMY_TYPE_HOCT_UP = 13,
WHAMMY_TYPE_HOCT_DN = 14,
WHAMMY_TYPE_OCT_GT_D = 15,
};
#define PITCH_AMOUNT 1730
#define PITCH_BALANCE 1733
#define PITCH_MIX 1745
@@ -598,6 +691,47 @@ enum {
#define IPS_SCALE 2755
#define IPS_LEVEL 2757
enum {
IPS_KEY_E = 0,
IPS_KEY_F = 1,
IPS_KEY_Gb = 2,
IPS_KEY_G = 3,
IPS_KEY_Ab = 4,
IPS_KEY_A = 5,
IPS_KEY_Bb = 6,
IPS_KEY_B = 7,
IPS_KEY_C = 8,
IPS_KEY_Db = 9,
IPS_KEY_D = 10,
IPS_KEY_Eb = 11,
};
enum {
IPS_SHIFT_OCT_DN = 0,
IPS_SHIFT_7TH_DN = 1,
IPS_SHIFT_6TH_DN = 2,
IPS_SHIFT_5TH_DN = 3,
IPS_SHIFT_4TH_DN = 4,
IPS_SHIFT_3RD_DN = 5,
IPS_SHIFT_2ND_DN = 6,
IPS_SHIFT_2ND_UP = 7,
IPS_SHIFT_3RD_UP = 8,
IPS_SHIFT_4TH_UP = 9,
IPS_SHIFT_5TH_UP = 10,
IPS_SHIFT_6TH_UP = 11,
IPS_SHIFT_7TH_UP = 12,
IPS_SHIFT_OCT_UP = 13,
};
enum {
IPS_SCALE_MAJOR = 0,
IPS_SCALE_MINOR = 1,
IPS_SCALE_DORIAN = 2,
IPS_SCALE_MIXOLYDIAN = 3,
IPS_SCALE_LYDIAN = 4,
IPS_SCALE_HMINOR = 5,
};
#define OCTAVER_OCTAVE1 1746
#define OCTAVER_OCTAVE2 1747
#define OCTAVER_DRY_LEVEL 1748
@@ -608,6 +742,8 @@ enum {
DELAY_TYPE_MODULATED = 1047,
DELAY_TYPE_PONG = 1048,
DELAY_TYPE_TAPE = 1049,
DELAY_TYPE_ECHOPLEX = 1050,
DELAY_TYPE_DM = 1051,
DELAY_RP500_TYPE_DIGITAL = 1052,
DELAY_RP500_TYPE_ANALOG = 1053,
@@ -618,6 +754,9 @@ enum {
DELAY_RP500_TYPE_REVERSE = 1064,
DELAY_RP500_TYPE_TAPE = 1056,
DELAY_RP1000_TYPE_LO_FI = 1065,
DELAY_RP1000_TYPE_2_TAP = 1066,
DELAY_GNX3K_TYPE_MONO = 1027,
DELAY_GNX3K_TYPE_PINGPONG = 1028,
DELAY_GNX3K_TYPE_ANALOG = 1029,
@@ -631,6 +770,13 @@ enum {
#define DELAY_TIME 1888
#define DELAY_MULTIPLIER 1904
#define DELAY_3_QUARTR 2180
#define DELAY_EIGHT 2179
#define DELAY_DOTEIGHT 2178
#define DELAY_QUARTER 2177
#define DELAY_HALF 2176
#define DELAY_TAPE_WOW 1891
#define DELAY_TAPE_FLUTTER 1892
@@ -640,8 +786,10 @@ enum {
#define DELAY_DUCK_THRESH 1889
#define DELAY_DUCK_LEVEL 1890
#define DELAY_REPEAT_RATE 1898
#define DELAY_REPEAT_RATE_DM 1894
#define DELAY_ECHO 1895
#define DELAY_INTENSITY 1896
#define DELAY_ECHOPLEX_TIME 1897
#define DELAY_TIME_0_760 1899
#define DELAY_VOLUME 1893
#define DELAY_REPEATS_0_99 1874
@@ -649,6 +797,7 @@ enum {
#define DELAY_TAP_TIME_0_4990 1900
#define DELAY_MIX 1902
#define DELAY_TIME_0_4650 1901
#define DELAY_TAP_RATIO 1905
#define GNX3K_DELAY_TIME 1862
#define GNX3K_DELAY_FEEDBACK 1863
@@ -689,16 +838,58 @@ enum {
#define REVERB_LEVEL 1925
#define EXP_TYPE 8194
#define EXP_ASSIGN1 8194
#define EXP_POSITION 19
#define EXP_VOLUME_PRE_FX 854594
#define EXP_VOLUME_POST_FX 1116738
#define EXP_MIN 8195
#define EXP_MAX 8196
#define LFO_TYPE 8258
#define LFO_MIN 8259
#define LFO_MAX 8260
#define LFO_SPEED 8262
#define LFO_WAVEFORM 8263
#define LFO_WAVEFORM_TRI 0
#define LFO_WAVEFORM_SINE 1
#define LFO_WAVEFORM_SQUARE 2
#define LFO1_POSITION 22
#define LFO2_POSITION 23
// The LFO and VSWITCH defines are temporary.
#define LFO_ASSIGN_NONE 0
#define LFO_ASSIGN_PICKUP_ON 131137
#define LFO_ASSIGN_COMP_ON 262337
#define LFO_ASSIGN_SUSTAN 262352
#define VSWITCH_ASSIGN 8384
#define VSWITCH_MIN 8385
#define VSWITCH_MAX 8386
#define VSWITCH_TYPE 8393
#define VSWITCH_ENABLE 8449
#define VSWITCH_ASSIGN_POSITION 21
#define VSWITCH_TYPE_TOGGLE 0
#define VSWITCH_PEDAL_ASSIGN_WAH 196740
#define VSWITCH_ASSIGN_WAH_ENABLE 196737
#define LIBRARY_TONE 8704
#define LIBRARY_EFFECTS 8705
#define EFFECTS_LEVEL 8706
#define LIBRARY_POSITION 25
#define LIB_POSITION 26
#define TONE_LIB_TYPE 8704
#define FX_LIB_TYPE 8705
#define FX_LIB_LEVEL 8706
#define FX_LIB_LEVEL_MAX1 8708
#define FX_LIB_LEVEL_MAX2 8710
#define FX_LIB_LEVEL_MAX3 8712
enum {
TONE_LIB_OVERDRIVE = 1793,
@@ -865,25 +1056,72 @@ typedef enum {
NACK = 0x7F
} MessageID;
typedef enum {
SECTION_GENETX_AMP = 0,
SECTION_GENETX_CABINET = 1,
SECTION_DRUM_PATTERN = 2,
SECTION_DRUM_KIT = 3,
SECTION_DRUM_SAMPLES = 4,
SECTION_SONG = 5,
SECTION_FOOTSWITCH = 6,
SECTION_GENETX2_AMP = 7,
SECTION_GENETX2_CABINET = 8,
SECTION_DEVICE_GUID = 9,
SECTION_DEVICE_NAME = 10,
} SectionID;
typedef struct {
int id;
int position;
int value;
} SettingParam;
enum {
GENETX_VERSION_1,
GENETX_VERSION_2
};
enum {
GENETX_TYPE_NOT_SET = -1,
GENETX_TYPE_AMP,
GENETX_TYPE_CABINET
};
enum {
GENETX_CHANNEL1 = 0,
GENETX_CHANNEL2 = 1,
GENETX_CHANNEL1_CUSTOM = 2,
GENETX_CHANNEL2_CUSTOM = 3,
GENETX_CHANNEL_CURRENT = 4
} ChannelBankIndex;
typedef struct {
int version;
int type;
int channel;
gchar *name;
GString *data;
} SettingGenetx;
void send_message(gint procedure, gchar *data, gint len);
MessageID get_message_id(GString *msg);
void append_value(GString *msg, guint value);
GString *get_message_by_id(MessageID id);
SettingParam *setting_param_new();
SettingParam *setting_param_new_from_data(gchar *str, gint *len);
SettingGenetx *setting_genetx_new();
void setting_genetx_free(SettingGenetx *genetx);
void setting_param_free(SettingParam *param);
SectionID get_genetx_section_id(gint version, gint type);
void set_option(guint id, guint position, guint value);
void send_object(SectionID section, guint bank, guint index,
gchar *name, GString *data);
void send_preset_parameters(GList *params);
void switch_preset(guint bank, guint x);
void store_preset_name(int x, const gchar *name);
void set_preset_level(int level);
GStrv query_preset_names(gchar bank);
void message_list_free(GList *list);
GList *get_current_preset();
void preset_list_free(GList *list);
#endif /* GDIGI_H */

39
gdigi_xml.h Normal file
View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2009 Tomasz Moń <desowin@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; under version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses>.
*/
#ifndef GDIGI_XML_H
#define GDIGI_XML_H
#include <glib.h>
#include "effects.h"
typedef struct {
guint type;
gchar *label;
} XmlLabel;
typedef struct {
guint id;
guint position;
gchar *label;
EffectValues *values;
XmlLabel *xml_labels; /* 'type' id's have a label group. */
guint xml_labels_amt;
} XmlSettings;
// XmlSettings *get_xml_settings(guint id, guint position);
// gchar *get_xml_label(guint id, guint position, gint type);
#endif /* GDIGI_XML_H */

414
gtkknob.c
View File

@@ -4,7 +4,7 @@
* Tony Garnock-Jones, with modifications from Sean Bolton,
* copyright (C) 2004, William Weston copyright (C) 2007,
* Pete Shorthose copyright (C) 2007, and Tomasz Moń,
* copyright (C) 2009
* copyright (C) 2009-2011
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -24,8 +24,9 @@
#include <math.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <string.h>
#include "gtkknob.h"
#include "knob.h"
#ifndef M_PI
# define M_PI 3.14159265358979323846 /* pi */
@@ -44,11 +45,15 @@
static void gtk_knob_class_init(GtkKnobClass *klass);
static void gtk_knob_init(GtkKnob *knob);
static void gtk_knob_destroy(GtkObject *object);
static void gtk_knob_destroy(GtkWidget *object);
static void gtk_knob_realize(GtkWidget *widget);
static void gtk_knob_size_request(GtkWidget *widget, GtkRequisition *requisition);
static void gtk_knob_unrealize(GtkWidget *widget);
static void gtk_knob_map(GtkWidget *widget);
static void gtk_knob_unmap(GtkWidget *widget);
static void gtk_knob_get_preferred_width(GtkWidget *widget, gint *minimum_width, gint *natural_width);
static void gtk_knob_get_preferred_height(GtkWidget *widget, gint *minimum_height, gint *natural_height);
static void gtk_knob_size_allocate(GtkWidget *widget, GtkAllocation *allocation);
static gint gtk_knob_expose(GtkWidget *widget, GdkEventExpose *event);
static gboolean gtk_knob_draw(GtkWidget *widget, cairo_t *cr);
static gint gtk_knob_scroll(GtkWidget *widget, GdkEventScroll *event);
static gint gtk_knob_button_press(GtkWidget *widget, GdkEventButton *event);
static gint gtk_knob_button_release(GtkWidget *widget, GdkEventButton *event);
@@ -102,25 +107,27 @@ gtk_knob_get_type(void) {
*****************************************************************************/
static void
gtk_knob_class_init (GtkKnobClass *klass) {
GtkObjectClass *object_class;
GtkWidgetClass *widget_class;
object_class = (GtkObjectClass*) klass;
widget_class = (GtkWidgetClass*) klass;
parent_class = g_type_class_peek_parent(klass);
object_class->destroy = gtk_knob_destroy;
widget_class->destroy = gtk_knob_destroy;
widget_class->realize = gtk_knob_realize;
widget_class->expose_event = gtk_knob_expose;
widget_class->size_request = gtk_knob_size_request;
widget_class->unrealize = gtk_knob_unrealize;
widget_class->draw = gtk_knob_draw;
widget_class->get_preferred_width = gtk_knob_get_preferred_width;
widget_class->get_preferred_height = gtk_knob_get_preferred_height;
widget_class->size_allocate = gtk_knob_size_allocate;
widget_class->scroll_event = gtk_knob_scroll;
widget_class->button_press_event = gtk_knob_button_press;
widget_class->button_release_event = gtk_knob_button_release;
widget_class->key_press_event = gtk_knob_key_press;
widget_class->motion_notify_event = gtk_knob_motion_notify;
widget_class->map = gtk_knob_map;
widget_class->unmap = gtk_knob_unmap;
}
@@ -131,19 +138,21 @@ gtk_knob_class_init (GtkKnobClass *klass) {
*****************************************************************************/
static void
gtk_knob_init (GtkKnob *knob) {
knob->policy = GTK_UPDATE_CONTINUOUS;
knob->policy = GTK_KNOB_UPDATE_CONTINUOUS;
knob->state = STATE_IDLE;
knob->saved_x = 0;
knob->saved_y = 0;
knob->timer = 0;
knob->anim = NULL;
knob->mask = NULL;
knob->mask_gc = NULL;
knob->red_gc = NULL;
knob->old_value = 0.0;
knob->old_lower = 0.0;
knob->old_upper = 0.0;
knob->adjustment = NULL;
gtk_widget_set_can_focus(GTK_WIDGET(knob), TRUE);
gtk_widget_set_has_window(GTK_WIDGET(knob), FALSE);
gtk_widget_set_hexpand(GTK_WIDGET(knob), FALSE);
gtk_widget_set_vexpand(GTK_WIDGET(knob), FALSE);
}
@@ -157,7 +166,6 @@ gtk_knob_new(GtkAdjustment *adjustment, GtkKnobAnim *anim) {
GtkKnob *knob;
g_return_val_if_fail (anim != NULL, NULL);
g_return_val_if_fail (GDK_IS_PIXBUF (anim->pixbuf), NULL);
knob = g_object_new (gtk_knob_get_type (), NULL);
@@ -180,7 +188,7 @@ gtk_knob_new(GtkAdjustment *adjustment, GtkKnobAnim *anim) {
*
*****************************************************************************/
static void
gtk_knob_destroy(GtkObject *object) {
gtk_knob_destroy(GtkWidget *object) {
GtkKnob *knob;
g_return_if_fail (object != NULL);
@@ -196,22 +204,8 @@ gtk_knob_destroy(GtkObject *object) {
}
*/
if (knob->mask) {
g_object_unref (knob->mask);
knob->mask = NULL;
}
if (knob->mask_gc) {
g_object_unref (knob->mask_gc);
knob->mask_gc = NULL;
}
if (knob->red_gc) {
g_object_unref (knob->red_gc);
knob->red_gc = NULL;
}
if (GTK_OBJECT_CLASS (parent_class)->destroy) {
(*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
if (GTK_WIDGET_CLASS(parent_class)->destroy) {
(*GTK_WIDGET_CLASS (parent_class)->destroy) (object);
}
}
@@ -237,7 +231,7 @@ gtk_knob_get_adjustment(GtkKnob *knob) {
*
*****************************************************************************/
void
gtk_knob_set_update_policy(GtkKnob *knob, GtkUpdateType policy) {
gtk_knob_set_update_policy(GtkKnob *knob, GtkKnobUpdateType policy) {
g_return_if_fail (knob != NULL);
g_return_if_fail (GTK_IS_KNOB (knob));
@@ -276,9 +270,9 @@ gtk_knob_set_adjustment(GtkKnob *knob, GtkAdjustment *adjustment) {
G_CALLBACK(gtk_knob_adjustment_value_changed),
knob);
knob->old_value = adjustment->value;
knob->old_lower = adjustment->lower;
knob->old_upper = adjustment->upper;
knob->old_value = gtk_adjustment_get_value(adjustment);
knob->old_lower = gtk_adjustment_get_lower(adjustment);
knob->old_upper = gtk_adjustment_get_upper(adjustment);
gtk_knob_update (knob);
}
@@ -293,66 +287,98 @@ gtk_knob_set_adjustment(GtkKnob *knob, GtkAdjustment *adjustment) {
static void
gtk_knob_realize(GtkWidget *widget) {
GtkKnob *knob;
GdkWindow *window;
GdkWindowAttr attributes;
GtkAllocation allocation;
gint attributes_mask;
GdkColor color = { 0, 0xffff, 0, 0 };
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_KNOB (widget));
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
gtk_widget_get_allocation (widget, &allocation);
gtk_widget_set_realized(widget, TRUE);
knob = GTK_KNOB (widget);
attributes.x = widget->allocation.x;
attributes.y = widget->allocation.y;
attributes.width = widget->allocation.width;
attributes.height = widget->allocation.height;
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.window_type = GDK_WINDOW_CHILD;
attributes.x = allocation.x;
attributes.y = allocation.y;
attributes.width = allocation.width;
attributes.height = allocation.height;
attributes.wclass = GDK_INPUT_ONLY;;
attributes.event_mask =
gtk_widget_get_events (widget) |
GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
GDK_POINTER_MOTION_HINT_MASK;
attributes.visual = gtk_widget_get_visual (widget);
attributes.colormap = gtk_widget_get_colormap (widget);
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
attributes_mask = GDK_WA_X | GDK_WA_Y;
widget->window = gdk_window_new (widget->parent->window,
&attributes, attributes_mask);
window = gtk_widget_get_parent_window(widget);
gtk_widget_set_window(widget, window);
g_object_ref(window);
widget->style = gtk_style_attach (widget->style, widget->window);
gdk_window_set_user_data (widget->window, widget);
gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
knob->mask_gc = gdk_gc_new (widget->window);
gdk_gc_copy (knob->mask_gc, widget->style->bg_gc[GTK_STATE_NORMAL]);
gdk_gc_set_clip_mask (knob->mask_gc, knob->mask);
knob->red_gc = gdk_gc_new (widget->window);
gdk_gc_copy (knob->red_gc, widget->style->bg_gc[GTK_STATE_NORMAL]);
gdk_colormap_alloc_color (attributes.colormap, &color, FALSE, TRUE);
gdk_gc_set_foreground (knob->red_gc, &color);
knob->event_window = gdk_window_new(window, &attributes, attributes_mask);
gdk_window_set_user_data(knob->event_window, knob);
}
/*****************************************************************************
*
* gtk_knob_size_request()
*
*****************************************************************************/
static void
gtk_knob_size_request (GtkWidget *widget, GtkRequisition *requisition) {
gtk_knob_unrealize(GtkWidget *widget)
{
GtkKnob *knob = GTK_KNOB(widget);
if (knob->event_window)
{
gdk_window_set_user_data(knob->event_window, NULL);
gdk_window_destroy(knob->event_window);
knob->event_window = NULL;
}
GTK_WIDGET_CLASS(parent_class)->unrealize(widget);
}
static void
gtk_knob_map(GtkWidget *widget)
{
GtkKnob *knob = GTK_KNOB(widget);
GTK_WIDGET_CLASS(parent_class)->map(widget);
if (knob->event_window)
gdk_window_show(knob->event_window);
}
static void
gtk_knob_unmap(GtkWidget *widget)
{
GtkKnob *knob = GTK_KNOB(widget);
if (knob->event_window)
gdk_window_hide(knob->event_window);
GTK_WIDGET_CLASS(parent_class)->unmap(widget);
}
static void
gtk_knob_get_preferred_width(GtkWidget *widget,
gint *minimum_width,
gint *natural_width)
{
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_KNOB (widget));
requisition->width = GTK_KNOB (widget)->width;
requisition->height = GTK_KNOB (widget)->height;
*minimum_width = *natural_width = GTK_KNOB(widget)->width;
}
static void
gtk_knob_get_preferred_height(GtkWidget *widget,
gint *minimum_height,
gint *natural_height)
{
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_KNOB (widget));
*minimum_height = *natural_height = GTK_KNOB(widget)->height;
}
@@ -369,11 +395,11 @@ gtk_knob_size_allocate (GtkWidget *widget, GtkAllocation *allocation) {
g_return_if_fail (GTK_IS_KNOB (widget));
g_return_if_fail (allocation != NULL);
widget->allocation = *allocation;
gtk_widget_set_allocation(widget, allocation);
knob = GTK_KNOB (widget);
if (GTK_WIDGET_REALIZED (widget)) {
gdk_window_move_resize (widget->window,
if (gtk_widget_get_realized (widget)) {
gdk_window_move_resize (knob->event_window,
allocation->x, allocation->y,
allocation->width, allocation->height);
}
@@ -382,39 +408,50 @@ gtk_knob_size_allocate (GtkWidget *widget, GtkAllocation *allocation) {
/*****************************************************************************
*
* gtk_knob_expose()
* gtk_knob_draw()
*
*****************************************************************************/
static gint
gtk_knob_expose(GtkWidget *widget, GdkEventExpose *event) {
static gboolean
gtk_knob_draw(GtkWidget *widget, cairo_t *cr) {
GtkKnob *knob;
gfloat dx, dy;
gdouble dx, dy;
gint frames;
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_KNOB (widget), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
g_return_val_if_fail (cr != NULL, FALSE);
g_return_val_if_fail (GTK_IS_ADJUSTMENT (GTK_KNOB (widget)->adjustment), FALSE);
if (event->count > 0)
return FALSE;
knob = GTK_KNOB (widget);
frames = ((knob->anim->width / knob->anim->frame_width) - 1);
dx = knob->adjustment->value - knob->adjustment->lower; /* value, from 0 */
dy = knob->adjustment->upper - knob->adjustment->lower; /* range */
dx = gtk_adjustment_get_value(knob->adjustment) - gtk_adjustment_get_lower(knob->adjustment); /* value, from 0 */
dy = gtk_adjustment_get_upper(knob->adjustment) - gtk_adjustment_get_lower(knob->adjustment); /* range */
dx = (int)(frames * dx / dy) * knob->width; /* check this for height != width */
gdk_draw_pixbuf (widget->window, knob->mask_gc, knob->anim->pixbuf,
dx, 0, 0, 0, knob->width, knob->height,
GDK_RGB_DITHER_NONE, 0, 0);
cairo_surface_t *surface =
cairo_surface_create_for_rectangle(knob->anim->image,
dx, 0.0,
(double)knob->width,
(double)knob->height);
cairo_set_source_surface(cr, surface, 0, 0);
cairo_paint(cr);
cairo_surface_destroy(surface);
if (GTK_WIDGET_HAS_FOCUS(widget)) {
gtk_paint_focus (widget->style, widget->window, widget->state,
NULL, widget, NULL, 0, 0,
widget->allocation.width, widget->allocation.height);
if (gtk_widget_has_focus(widget)) {
GtkStyleContext *context;
context = gtk_widget_get_style_context(widget);
gtk_style_context_save(context);
gtk_style_context_set_state(context, gtk_widget_get_state_flags (widget));
cairo_save(cr);
gtk_render_focus(context, cr,
0, 0,
gtk_widget_get_allocated_width(widget),
gtk_widget_get_allocated_height(widget));
cairo_restore(cr);
gtk_style_context_restore(context);
}
return FALSE;
@@ -439,11 +476,15 @@ gtk_knob_scroll(GtkWidget *widget, GdkEventScroll *event) {
switch (event->direction) {
case GDK_SCROLL_UP:
knob->adjustment->value += knob->adjustment->step_increment;
gtk_adjustment_set_value(knob->adjustment,
gtk_adjustment_get_value(knob->adjustment) +
gtk_adjustment_get_step_increment(knob->adjustment));
g_signal_emit_by_name (knob->adjustment, "value_changed");
break;
case GDK_SCROLL_DOWN:
knob->adjustment->value -= knob->adjustment->step_increment;
gtk_adjustment_set_value(knob->adjustment,
gtk_adjustment_get_value(knob->adjustment) -
gtk_adjustment_get_step_increment(knob->adjustment));
g_signal_emit_by_name (knob->adjustment, "value_changed");
break;
default:
@@ -475,16 +516,17 @@ gtk_knob_button_press(GtkWidget *widget, GdkEventButton *event) {
switch (event->button) {
case 1:
case 3:
if (!GTK_WIDGET_HAS_FOCUS(widget))
if (!gtk_widget_has_focus(widget))
gtk_widget_grab_focus(widget);
knob->state = STATE_PRESSED;
knob->saved_x = event->x;
knob->saved_y = event->y;
break;
case 2:
knob->adjustment->value = floor ((knob->adjustment->lower +
knob->adjustment->upper + 1.0)
* 0.5);
gtk_adjustment_set_value(knob->adjustment,
floor ((gtk_adjustment_get_lower(knob->adjustment) +
gtk_adjustment_get_upper(knob->adjustment) + 1.0)
* 0.5));
g_signal_emit_by_name (knob->adjustment, "value_changed");
break;
}
@@ -523,8 +565,8 @@ gtk_knob_button_release(GtkWidget *widget, GdkEventButton *event) {
switch (event->button) {
case 1:
case 3:
if (knob->policy != GTK_UPDATE_CONTINUOUS
&& knob->old_value != knob->adjustment->value)
if (knob->policy != GTK_KNOB_UPDATE_CONTINUOUS
&& knob->old_value != gtk_adjustment_get_value(knob->adjustment))
{
g_signal_emit_by_name (knob->adjustment, "value_changed");
}
@@ -550,20 +592,20 @@ static gint gtk_knob_key_press(GtkWidget *widget, GdkEventKey *event)
switch (event->keyval) {
case GDK_Up:
if (GTK_WIDGET_HAS_FOCUS (widget))
case GDK_KEY_Up:
if (gtk_widget_has_focus (widget))
{
gtk_adjustment_set_value (knob->adjustment,
knob->old_value + knob->adjustment->step_increment);
knob->old_value + gtk_adjustment_get_step_increment(knob->adjustment));
return TRUE;
}
return FALSE;
case GDK_Down:
if (GTK_WIDGET_HAS_FOCUS (widget))
case GDK_KEY_Down:
if (gtk_widget_has_focus (widget))
{
gtk_adjustment_set_value (knob->adjustment,
knob->old_value - knob->adjustment->step_increment);
knob->old_value - gtk_adjustment_get_step_increment(knob->adjustment));
return TRUE;
}
return FALSE;
@@ -594,8 +636,8 @@ gtk_knob_motion_notify(GtkWidget *widget, GdkEventMotion *event) {
x = event->x;
y = event->y;
if (event->is_hint || (event->window != widget->window)) {
gdk_window_get_pointer (widget->window, &x, &y, &mods);
if (event->is_hint || (event->window != gtk_widget_get_window(widget))) {
gdk_window_get_pointer(gtk_widget_get_window(widget), &x, &y, &mods);
}
switch (knob->state) {
@@ -632,7 +674,7 @@ gtk_knob_timer(GtkKnob *knob) {
g_return_val_if_fail (GTK_IS_KNOB (knob), FALSE);
g_return_val_if_fail (GTK_IS_ADJUSTMENT (knob->adjustment), FALSE);
if (knob->policy == GTK_UPDATE_DELAYED) {
if (knob->policy == GTK_KNOB_UPDATE_DELAYED) {
g_signal_emit_by_name (knob->adjustment, "value_changed");
}
@@ -650,13 +692,13 @@ static void
gtk_knob_update_mouse_update(GtkKnob *knob) {
g_return_if_fail(GTK_IS_ADJUSTMENT (knob->adjustment));
if (knob->policy == GTK_UPDATE_CONTINUOUS) {
if (knob->policy == GTK_KNOB_UPDATE_CONTINUOUS) {
g_signal_emit_by_name (knob->adjustment, "value_changed");
}
else {
gtk_widget_queue_draw (GTK_WIDGET (knob));
if (knob->policy == GTK_UPDATE_DELAYED) {
if (knob->policy == GTK_KNOB_UPDATE_DELAYED) {
if (knob->timer) {
g_source_remove (knob->timer);
}
@@ -675,14 +717,14 @@ gtk_knob_update_mouse_update(GtkKnob *knob) {
*****************************************************************************/
static void
gtk_knob_update_mouse(GtkKnob *knob, gint x, gint y, gboolean step) {
gfloat old_value, new_value, dv, dh;
gfloat angle;
gdouble old_value, new_value, dv, dh;
gdouble angle;
g_return_if_fail (knob != NULL);
g_return_if_fail (GTK_IS_KNOB (knob));
g_return_if_fail (GTK_IS_ADJUSTMENT (knob->adjustment));
old_value = knob->adjustment->value;
old_value = gtk_adjustment_get_value(knob->adjustment);
angle = atan2f (-y + (knob->height >> 1), x - (knob->width >> 1));
@@ -699,17 +741,17 @@ gtk_knob_update_mouse(GtkKnob *knob, gint x, gint y, gboolean step) {
dh *= angle * angle;
}
new_value = knob->adjustment->value +
dv * (step ? knob->adjustment->step_increment : knob->adjustment->page_increment) +
dh * (knob->adjustment->upper -
knob->adjustment->lower) * 0.005; /* 0.005 == (1 / 200) */
new_value = gtk_adjustment_get_value(knob->adjustment) +
dv * (step ? gtk_adjustment_get_step_increment(knob->adjustment) : gtk_adjustment_get_page_increment(knob->adjustment)) +
dh * (gtk_adjustment_get_upper(knob->adjustment) -
gtk_adjustment_get_lower(knob->adjustment)) * 0.005; /* 0.005 == (1 / 200) */
new_value = MAX (MIN (new_value, knob->adjustment->upper),
knob->adjustment->lower);
new_value = MAX (MIN (new_value, gtk_adjustment_get_upper(knob->adjustment)),
gtk_adjustment_get_lower(knob->adjustment));
knob->adjustment->value = new_value;
gtk_adjustment_set_value(knob->adjustment, new_value);
if (knob->adjustment->value != old_value) {
if (gtk_adjustment_get_value(knob->adjustment) != old_value) {
gtk_knob_update_mouse_update (knob);
}
}
@@ -722,29 +764,29 @@ gtk_knob_update_mouse(GtkKnob *knob, gint x, gint y, gboolean step) {
*****************************************************************************/
static void
gtk_knob_update(GtkKnob *knob) {
gfloat new_value;
gdouble new_value;
g_return_if_fail (knob != NULL);
g_return_if_fail (GTK_IS_KNOB (knob));
g_return_if_fail (GTK_IS_ADJUSTMENT (knob->adjustment));
if (knob->adjustment->step_increment == 1) {
new_value = floor (knob->adjustment->value + 0.5);
if (gtk_adjustment_get_step_increment(knob->adjustment) == 1) {
new_value = floor (gtk_adjustment_get_value(knob->adjustment) + 0.5);
}
else {
new_value = knob->adjustment->value;
new_value = gtk_adjustment_get_value(knob->adjustment);
}
if (new_value < knob->adjustment->lower) {
new_value = knob->adjustment->lower;
if (new_value < gtk_adjustment_get_lower(knob->adjustment)) {
new_value = gtk_adjustment_get_lower(knob->adjustment);
}
if (new_value > knob->adjustment->upper) {
new_value = knob->adjustment->upper;
if (new_value > gtk_adjustment_get_upper(knob->adjustment)) {
new_value = gtk_adjustment_get_upper(knob->adjustment);
}
if (new_value != knob->adjustment->value) {
knob->adjustment->value = new_value;
if (new_value != gtk_adjustment_get_value(knob->adjustment)) {
gtk_adjustment_set_value(knob->adjustment, new_value);
g_signal_emit_by_name (knob->adjustment, "value_changed");
}
@@ -766,15 +808,15 @@ gtk_knob_adjustment_changed(GtkAdjustment *adjustment, gpointer data) {
knob = GTK_KNOB (data);
if ((knob->old_value != adjustment->value) ||
(knob->old_lower != adjustment->lower) ||
(knob->old_upper != adjustment->upper))
if ((knob->old_value != gtk_adjustment_get_value(adjustment)) ||
(knob->old_lower != gtk_adjustment_get_lower(adjustment)) ||
(knob->old_upper != gtk_adjustment_get_upper(adjustment)))
{
gtk_knob_update (knob);
knob->old_value = adjustment->value;
knob->old_lower = adjustment->lower;
knob->old_upper = adjustment->upper;
knob->old_value = gtk_adjustment_get_value(adjustment);
knob->old_lower = gtk_adjustment_get_lower(adjustment);
knob->old_upper = gtk_adjustment_get_upper(adjustment);
}
}
@@ -793,9 +835,9 @@ gtk_knob_adjustment_value_changed (GtkAdjustment *adjustment, gpointer data) {
knob = GTK_KNOB (data);
if (knob->old_value != adjustment->value) {
if (knob->old_value != gtk_adjustment_get_value(adjustment)) {
gtk_knob_update (knob);
knob->old_value = adjustment->value;
knob->old_value = gtk_adjustment_get_value(adjustment);
}
}
@@ -810,29 +852,31 @@ gtk_knob_set_animation (GtkKnob *knob, GtkKnobAnim *anim) {
g_return_if_fail (knob != NULL);
g_return_if_fail (anim != NULL);
g_return_if_fail (GTK_IS_KNOB (knob));
g_return_if_fail (GDK_IS_PIXBUF (anim->pixbuf));
knob->anim = (GtkKnobAnim *)anim;
knob->width = anim->frame_width;
knob->height = anim->height;
if (GTK_WIDGET_REALIZED (knob)) {
if (gtk_widget_get_realized (GTK_WIDGET(knob))) {
gtk_widget_queue_resize (GTK_WIDGET (knob));
}
}
/**
* Reads embedded knob image
**/
static cairo_status_t
get_knob_image(void *closure, unsigned char *data, unsigned int length)
{
int *offset = (int *)closure;
/*****************************************************************************
*
* gtk_knob_animation_new_from_file()
*
*****************************************************************************/
GtkKnobAnim *
gtk_knob_animation_new_from_file(gchar *filename) {
GtkKnobAnim *anim;
if ((*offset + length) > sizeof (knob_png))
return CAIRO_STATUS_READ_ERROR;
anim = gtk_knob_animation_new_from_file_full (filename, -1, -1, KNOB_SIZE);
return anim;
memcpy (data, knob_png + *offset, length);
*offset = *offset + length;
return CAIRO_STATUS_SUCCESS;
}
/*****************************************************************************
@@ -841,68 +885,20 @@ gtk_knob_animation_new_from_file(gchar *filename) {
*
*****************************************************************************/
GtkKnobAnim *
gtk_knob_animation_new_from_inline(const guint8 *pixbuf) {
gtk_knob_animation_new_from_inline() {
GtkKnobAnim *anim = g_new0 (GtkKnobAnim, 1);
int offset = 0;
g_return_val_if_fail((pixbuf != NULL), NULL);
anim->image =
cairo_image_surface_create_from_png_stream(get_knob_image, &offset);
anim->pixbuf = gdk_pixbuf_new_from_inline(-1, pixbuf, FALSE, NULL);
if (anim->pixbuf == NULL) {
g_free(anim);
return NULL;
}
anim->height = gdk_pixbuf_get_height (anim->pixbuf);
anim->width = gdk_pixbuf_get_width (anim->pixbuf);
anim->width = cairo_image_surface_get_width(anim->image);
anim->height = cairo_image_surface_get_height(anim->image);
anim->frame_width = anim->height;
return anim;
}
/*****************************************************************************
*
* gtk_knob_new_from_file_full()
*
* frame_width: overrides the frame width (to make rectangular frames)
* but doesn't affect the image size width and height cause optional
* scaling if not set to -1 when they are derived from the native
* image size.
*
* FIXME: account for any problems where (width % frame_width != 0)
*
*****************************************************************************/
GtkKnobAnim *
gtk_knob_animation_new_from_file_full(gchar *filename, gint frame_width,
gint width, gint height) {
GtkKnobAnim *anim = g_new0 (GtkKnobAnim, 1);
g_return_val_if_fail ((filename != NULL), NULL);
GError *gerror = NULL;
#if GTK_MINOR_VERSION < 10
if (!(anim->pixbuf = gdk_pixbuf_new_from_file (filename, &gerror))) {
g_error_free(gerror);
gerror = NULL;
return NULL;
}
#else /* GTK_MINOR_VERSION >= 10 */
if (!(anim->pixbuf = gdk_pixbuf_new_from_file_at_size (filename, width,
height, &gerror))) {
g_error_free(gerror);
gerror = NULL;
return NULL;
}
#endif /* GTK_MINOR_VERSION < 10 */
else {
anim->height = gdk_pixbuf_get_height (anim->pixbuf);
anim->width = gdk_pixbuf_get_width (anim->pixbuf);
anim->frame_width = (frame_width != -1) ? frame_width : anim->height;
}
return anim;
}
/*****************************************************************************
*
* gtk_knob_animation_free()
@@ -912,8 +908,8 @@ void
gtk_knob_animation_free(GtkKnobAnim *anim) {
g_return_if_fail (anim != NULL);
if (anim->pixbuf)
g_object_unref (anim->pixbuf);
if (anim->image)
cairo_surface_destroy(anim->image);
g_free (anim);
}

View File

@@ -3,7 +3,7 @@
* Most of this code comes from gAlan 0.2.0, copyright (C) 1999
* Tony Garnock-Jones, with modifications by Sean Bolton,
* copyright (C) 2004, and minor modifications by William Weston,
* copyright (C) 2007, Tomasz Moń, copyright (C) 2009
* copyright (C) 2007, Tomasz Moń, copyright (C) 2009-2011
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -36,9 +36,16 @@ typedef struct _GtkKnob GtkKnob;
typedef struct _GtkKnobClass GtkKnobClass;
typedef struct _GtkKnobAnim GtkKnobAnim;
typedef enum
{
GTK_KNOB_UPDATE_CONTINUOUS,
GTK_KNOB_UPDATE_DISCONTINUOUS,
GTK_KNOB_UPDATE_DELAYED
} GtkKnobUpdateType;
/* better to make this an object and let widgets ref/deref it perhaps */
struct _GtkKnobAnim {
GdkPixbuf *pixbuf;
cairo_surface_t *image;
gint width; /* derived from image width */
gint height; /* derived from image height. */
gint frame_width; /* derived from pixbuf (width / height) or provided override for rectangular frames */
@@ -47,6 +54,7 @@ typedef struct _GtkKnobAnim GtkKnobAnim;
struct _GtkKnob {
GtkWidget widget;
GdkWindow *event_window;
/* update policy (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */
guint policy : 2;
@@ -61,14 +69,10 @@ typedef struct _GtkKnobAnim GtkKnobAnim;
GtkKnobAnim *anim;
gint width, height;
GdkBitmap *mask;
GdkGC *mask_gc;
GdkGC *red_gc;
/* Old values from adjustment stored so we know when something changes */
gfloat old_value;
gfloat old_lower;
gfloat old_upper;
gdouble old_value;
gdouble old_lower;
gdouble old_upper;
/* The adjustment object that stores the data for this knob */
GtkAdjustment *adjustment;
@@ -82,16 +86,11 @@ typedef struct _GtkKnobAnim GtkKnobAnim;
extern GtkWidget *gtk_knob_new(GtkAdjustment *adjustment, GtkKnobAnim *anim);
extern GType gtk_knob_get_type(void);
extern GtkAdjustment *gtk_knob_get_adjustment(GtkKnob *knob);
extern void gtk_knob_set_update_policy(GtkKnob *knob, GtkUpdateType policy);
extern void gtk_knob_set_update_policy(GtkKnob *knob, GtkKnobUpdateType policy);
extern void gtk_knob_set_adjustment(GtkKnob *knob, GtkAdjustment *adjustment);
GtkKnobAnim *gtk_knob_animation_new_from_inline(const guint8 *pixbuf);
GtkKnobAnim *gtk_knob_animation_new_from_file_full(gchar *filename,
gint frame_width,
gint width,
gint height);
GtkKnobAnim *gtk_knob_animation_new_from_inline();
GtkKnobAnim *gtk_knob_animation_new_from_file(gchar *filename);
void gtk_knob_set_animation (GtkKnob *knob, GtkKnobAnim *anim);
void gtk_knob_animation_free(GtkKnobAnim *anim);

552
gui.c
View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009 Tomasz Moń <desowin@gmail.com>
* Copyright (c) 2009-2011 Tomasz Moń <desowin@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -15,15 +15,17 @@
*/
#include <gtk/gtk.h>
#include <glib-object.h>
#include <string.h>
#include <alsa/asoundlib.h>
#include "gdigi.h"
#include "gui.h"
#include "effects.h"
#include "preset.h"
#include "gtkknob.h"
#include "knob.h"
typedef struct {
GtkObject *widget;
GObject *widget;
/* used for combo boxes, if widget isn't combo box, then both value and x are -1 */
gint value; /**< effect type value */
@@ -50,12 +52,175 @@ void show_error_message(GtkWidget *parent, gchar *message)
GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
message);
"%s", message);
gtk_dialog_run(GTK_DIALOG(msg));
gtk_widget_destroy(msg);
}
/**
* \param value value to examine
* \param values EffectValues to check value against
*
* Examines whether value fits inside values range for given EffectValues.
*
* \return TRUE is value fits inside range, otherwise FALSE.
**/
static gboolean check_value_range(gint value, EffectValues *values)
{
if (((gint) values->min <= value) && (value <= (gint) values->max))
return TRUE;
else
return FALSE;
}
/**
* \param spin a GtkSpinButton
* \param new_val return value for valid input
* \param values signal user data, EffectValues for this parameter
*
* Custom GtkSpinButton "input" handler for EffectValues with non plain type.
*
* \return TRUE if new_val was set, otherwise FALSE.
**/
static gboolean custom_value_input_cb(GtkSpinButton *spin, gdouble *new_val, EffectValues *values)
{
gchar *text = g_strdup(gtk_entry_get_text(GTK_ENTRY(spin)));
gchar *err = NULL;
gdouble value;
for (;;) {
if (values->type & VALUE_TYPE_LABEL) {
/** search labels for value */
gint n;
for (n = 0; values->labels[n] != NULL; n++) {
if (g_strcmp0(values->labels[n], text) == 0) {
/* Value found */
*new_val = values->min + (gdouble)n;
g_free(text);
return TRUE;
}
}
/* Value not found */
if (values->type & VALUE_TYPE_EXTRA) {
values = values->extra;
continue;
} else {
g_free(text);
return FALSE;
}
}
if (values->type & VALUE_TYPE_SUFFIX) {
/* remove suffix from entry text */
gchar *tmp;
tmp = strstr(text, values->suffix);
if (tmp != NULL) {
gchar *temp = g_strndup(text, tmp - text);
g_free(text);
text = temp;
}
}
g_strstrip(text);
value = g_strtod(text, &err);
if (*err) {
if (values->type & VALUE_TYPE_EXTRA) {
values = values->extra;
continue;
} else {
g_free(text);
return FALSE;
}
}
if (values->type & VALUE_TYPE_STEP) {
value /= values->step;
}
if (values->type & VALUE_TYPE_OFFSET) {
value -= values->offset;
}
if (check_value_range((gint) value, values) == FALSE) {
if (values->type & VALUE_TYPE_EXTRA) {
values = values->extra;
continue;
} else {
g_free(text);
return FALSE;
}
}
*new_val = value;
g_free(text);
return TRUE;
}
}
/**
* \param spin a GtkSpinButton
* \param values signal user data, EffectValues for this parameter
*
* Custom GtkSpinButton "output" handler for EffectValues with non plain type.
*
* \return TRUE if text was set, otherwise FALSE.
**/
static gboolean custom_value_output_cb(GtkSpinButton *spin, EffectValues *values)
{
GtkAdjustment *adj;
gchar *text;
gdouble value;
adj = gtk_spin_button_get_adjustment(spin);
value = gtk_adjustment_get_value(adj);
while (check_value_range(value, values) == FALSE) {
if (values->type & VALUE_TYPE_EXTRA) {
values = values->extra;
} else {
g_message("custom_value_output_cb called with out of bounds value");
return FALSE;
}
}
if (values->type & VALUE_TYPE_LABEL) {
gtk_entry_set_text(GTK_ENTRY(spin), values->labels[(gint) value - (gint) values->min]);
return TRUE;
}
if (values->type & VALUE_TYPE_OFFSET) {
value += (gdouble) values->offset;
}
if (values->type & VALUE_TYPE_STEP) {
value *= values->step;
}
if (values->type & VALUE_TYPE_DECIMAL) {
text = g_strdup_printf("%.*f", values->decimal, value);
} else {
text = g_strdup_printf("%d", (gint) value);
}
if (values->type & VALUE_TYPE_SUFFIX) {
gchar *tmp;
tmp = g_strdup_printf("%s %s", text, values->suffix);
g_free(text);
text = tmp;
}
gtk_entry_set_text(GTK_ENTRY(spin), text);
g_free(text);
return TRUE;
}
/**
* \param adj the object which emitted the signal
* \param setting setting controlled by adj
@@ -71,20 +236,6 @@ void value_changed_option_cb(GtkAdjustment *adj, EffectSettings *setting)
g_object_get(G_OBJECT(adj), "value", &val, NULL);
set_option(setting->id, setting->position, (gint)val);
}
if (setting->values != NULL && setting->values->labels != NULL) {
GtkWidget *label;
gint x;
gdouble val = -1.0;
g_object_get(G_OBJECT(adj), "value", &val, NULL);
x = (gint)val;
if ((x >= setting->values->min) && (x <= setting->values->max)) {
label = g_object_get_data(G_OBJECT(adj), "label");
gtk_label_set_text(GTK_LABEL(label), setting->values->labels[x]);
}
}
}
/**
@@ -104,7 +255,7 @@ void toggled_cb(GtkToggleButton *button, Effect *effect)
}
/**
* \param widget GtkObject to add to widget tree
* \param widget GObject to add to widget tree
* \param id object controlled ID
* \param position object controlled position
* \param value effect value type (if widget is GtkComboBox, otherwise -1)
@@ -112,7 +263,7 @@ void toggled_cb(GtkToggleButton *button, Effect *effect)
*
* Adds widget to widget tree.
**/
static WidgetTreeElem *widget_tree_add(GtkObject *widget, gint id, gint position, gint value, gint x)
static WidgetTreeElem *widget_tree_add(GObject *widget, gint id, gint position, gint value, gint x)
{
GList *list;
WidgetTreeElem *el;
@@ -213,7 +364,7 @@ static void apply_current_preset()
{
GList *list = get_current_preset();
Preset *preset = create_preset_from_data(list);
preset_list_free(list);
message_list_free(list);
apply_preset_to_gui(preset);
preset_free(preset);
}
@@ -227,56 +378,64 @@ gboolean apply_current_preset_to_gui(gpointer data)
/**
* \param settings effect parameters
* \param amt amount of effect parameters
* \param widget_table hash table matching settings pointer with created table (may be NULL)
* \param widget_table hash table matching settings pointer with created grid (may be NULL)
*
* Creates knobs that allow user to set effect parameters.
*
* \return GtkTable containing necessary widgets to set effect parameters.
* \return GtkGrid containing necessary widgets to set effect parameters.
**/
GtkWidget *create_table(EffectSettings *settings, gint amt, GHashTable *widget_table)
GtkWidget *create_grid(EffectSettings *settings, gint amt, GHashTable *widget_table)
{
GtkWidget *table, *label, *widget, *knob;
GtkObject *adj;
GtkWidget *grid, *label, *widget, *knob;
GtkAdjustment *adj;
int x;
if (widget_table != NULL) {
table = g_hash_table_lookup(widget_table, settings);
if (table != NULL)
return table;
grid = g_hash_table_lookup(widget_table, settings);
if (grid != NULL)
return grid;
}
table = gtk_table_new(3, amt, FALSE);
grid = gtk_grid_new();
gtk_grid_set_row_spacing(GTK_GRID(grid), 2);
gtk_grid_set_column_spacing(GTK_GRID(grid), 2);
for (x = 0; x<amt; x++) {
gdouble min, max;
gboolean custom;
get_values_info(settings[x].values, &min, &max, &custom);
label = gtk_label_new(settings[x].label);
adj = gtk_adjustment_new(0.0, settings[x].values->min, settings[x].values->max,
adj = gtk_adjustment_new(0.0, min, max,
1.0, /* step increment */
MAX((settings[x].values->max / 100), 5.0), /* page increment */
MAX((max / 100), 5.0), /* page increment */
0.0);
knob = gtk_knob_new(GTK_ADJUSTMENT(adj), knob_anim);
if (settings[x].values->labels == NULL) {
widget = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 1.0, 0);
gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(widget), TRUE);
gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(widget), GTK_UPDATE_IF_VALID);
} else {
widget = gtk_label_new(settings[x].values->labels[0]);
g_object_set_data(G_OBJECT(adj), "label", widget);
widget = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 1.0, 0);
gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(widget), FALSE);
gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(widget), GTK_UPDATE_IF_VALID);
if (custom == TRUE) {
g_signal_connect(G_OBJECT(widget), "input", G_CALLBACK(custom_value_input_cb), settings[x].values);
g_signal_connect(G_OBJECT(widget), "output", G_CALLBACK(custom_value_output_cb), settings[x].values);
}
widget_tree_add(adj, settings[x].id, settings[x].position, -1, -1);
gtk_table_attach(GTK_TABLE(table), label, 0, 1, x, x+1, GTK_SHRINK, GTK_SHRINK, 2, 2);
gtk_table_attach(GTK_TABLE(table), knob, 1, 2, x, x+1, GTK_SHRINK, GTK_SHRINK, 2, 2);
gtk_table_attach(GTK_TABLE(table), widget, 2, 3, x, x+1, GTK_SHRINK, GTK_SHRINK, 2, 2);
widget_tree_add(G_OBJECT(adj), settings[x].id,
settings[x].position, -1, -1);
gtk_grid_attach(GTK_GRID(grid), label, 0, x, 1, 1);
gtk_grid_attach(GTK_GRID(grid), knob, 1, x, 1, 1);
gtk_grid_attach(GTK_GRID(grid), widget, 2, x, 1, 1);
g_signal_connect(G_OBJECT(adj), "value-changed", G_CALLBACK(value_changed_option_cb), &settings[x]);
if (widget_table != NULL) {
g_hash_table_insert(widget_table, settings, table);
g_hash_table_insert(widget_table, settings, grid);
}
}
return table;
return grid;
}
/**
@@ -295,7 +454,7 @@ GtkWidget *create_on_off_button(Effect *effect)
button = gtk_check_button_new_with_label(effect->label);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(toggled_cb), effect);
widget_tree_add(GTK_OBJECT(button), effect->id, effect->position, -1, -1);
widget_tree_add(G_OBJECT(button), effect->id, effect->position, -1, -1);
return button;
}
@@ -394,16 +553,17 @@ GtkWidget *create_widget_container(EffectGroup *group, gint amt, gint id, gint p
for (x = 0; x<amt; x++) {
if (group[x].label) {
if (combo_box == NULL) {
combo_box = gtk_combo_box_new_text();
gtk_container_add(GTK_CONTAINER(vbox), combo_box);
combo_box = gtk_combo_box_text_new();
gtk_box_pack_end(GTK_BOX(vbox), combo_box, FALSE, TRUE, 0);
g_signal_connect(G_OBJECT(combo_box), "changed", G_CALLBACK(combo_box_changed_cb), group);
g_object_set_data(G_OBJECT(combo_box), "vbox", vbox);
}
gtk_combo_box_append_text(GTK_COMBO_BOX(combo_box), group[x].label);
gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(combo_box),
NULL, group[x].label);
cmbox_no++;
if ((group[x].settings != NULL) && (group[x].settings_amt > 0)) {
widget = create_table(group[x].settings, group[x].settings_amt, widget_table);
widget = create_grid(group[x].settings, group[x].settings_amt, widget_table);
g_object_ref_sink(widget);
} else
widget = NULL;
@@ -414,15 +574,15 @@ GtkWidget *create_widget_container(EffectGroup *group, gint amt, gint id, gint p
settings->position = position;
settings->child = widget;
widget_tree_add(GTK_OBJECT(combo_box), id, position, group[x].type, x);
widget_tree_add(G_OBJECT(combo_box), id, position, group[x].type, x);
name = g_strdup_printf("SettingsGroup%d", cmbox_no);
g_object_set_data_full(G_OBJECT(combo_box), name, settings, ((GDestroyNotify)effect_settings_group_free));
g_free(name);
} else {
if ((group[x].settings != NULL) && (group[x].settings_amt > 0)) {
widget = create_table(group[x].settings, group[x].settings_amt, widget_table);
gtk_container_add(GTK_CONTAINER(vbox), widget);
widget = create_grid(group[x].settings, group[x].settings_amt, widget_table);
gtk_box_pack_end(GTK_BOX(vbox), widget, FALSE, TRUE, 0);
}
}
}
@@ -445,7 +605,7 @@ GtkWidget *create_vbox(Effect *widgets, gint amt, gchar *label)
{
GtkWidget *vbox;
GtkWidget *widget;
GtkWidget *table;
GtkWidget *grid;
GtkWidget *container;
GtkWidget *frame;
int x;
@@ -455,13 +615,13 @@ GtkWidget *create_vbox(Effect *widgets, gint amt, gchar *label)
vbox = gtk_vbox_new(FALSE, 0);
table = gtk_table_new(2, amt, FALSE);
gtk_table_set_col_spacings(GTK_TABLE(table), 2);
grid = gtk_grid_new();
gtk_grid_set_column_spacing(GTK_GRID(grid), 2);
for (x = 0; x<amt; x++) {
if ((widgets[x].id != -1) && (widgets[x].position != -1)) {
widget = create_on_off_button(&widgets[x]);
gtk_table_attach_defaults(GTK_TABLE(table), widget, 0, 1, x, x+1);
gtk_grid_attach(GTK_GRID(grid), widget, 0, x, 1, 1);
if (widgets[x].label)
y = 1;
@@ -470,14 +630,18 @@ GtkWidget *create_vbox(Effect *widgets, gint amt, gchar *label)
} else if (widgets[x].label) {
widget = gtk_label_new(widgets[x].label);
gtk_table_attach_defaults(GTK_TABLE(table), widget, 0, 1, x, x+1);
gtk_grid_attach(GTK_GRID(grid), widget, 0, x, 1, 1);
y = 0;
} else {
/* Default to 1 */
if (x == 0)
y = 1;
}
container = create_widget_container(widgets[x].group, widgets[x].group_amt, widgets[x].type, widgets[x].position);
gtk_table_attach_defaults(GTK_TABLE(table), container, 1-y, 2-y, x+y, x+y+1);
gtk_grid_attach(GTK_GRID(grid), container, 1-y, x+y, 1, 1);
}
gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 2);
gtk_box_pack_start(GTK_BOX(vbox), grid, FALSE, FALSE, 2);
gtk_container_add(GTK_CONTAINER(frame), vbox);
return frame;
@@ -609,7 +773,7 @@ GtkWidget *create_preset_tree(Device *device)
**/
static void show_store_preset_window(GtkWidget *window, gchar *default_name)
{
GtkWidget *dialog, *cmbox, *entry, *table, *label, *vbox;
GtkWidget *dialog, *cmbox, *entry, *grid, *label, *vbox;
GStrv names;
int x;
@@ -622,29 +786,29 @@ static void show_store_preset_window(GtkWidget *window, gchar *default_name)
vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
table = gtk_table_new(2, 2, FALSE);
gtk_container_add(GTK_CONTAINER(vbox), table);
grid = gtk_grid_new();
gtk_container_add(GTK_CONTAINER(vbox), grid);
cmbox = gtk_combo_box_new_text();
cmbox = gtk_combo_box_text_new();
names = query_preset_names(PRESETS_USER);
for (x=0; x<g_strv_length(names); x++) {
gchar *title = g_strdup_printf("%d - %s", x+1, names[x]);
gtk_combo_box_append_text(GTK_COMBO_BOX(cmbox), title);
gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(cmbox), NULL, title);
g_free(title);
}
g_strfreev(names);
gtk_table_attach_defaults(GTK_TABLE(table), cmbox, 1, 2, 0, 1);
gtk_grid_attach(GTK_GRID(grid), cmbox, 1, 0, 1, 1);
entry = gtk_entry_new();
if (default_name != NULL)
gtk_entry_set_text(GTK_ENTRY(entry), default_name);
gtk_table_attach_defaults(GTK_TABLE(table), entry, 1, 2, 1, 2);
gtk_grid_attach(GTK_GRID(grid), entry, 1, 1, 1, 1);
label = gtk_label_new("Preset slot:");
gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 1, 1);
label = gtk_label_new("Preset name:");
gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
gtk_grid_attach(GTK_GRID(grid), label, 0, 1, 1, 1);
gtk_widget_show_all(vbox);
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
@@ -677,10 +841,21 @@ static void action_show_about_dialog_cb(GtkAction *action)
{
static const gchar * const authors[] = {
"Tomasz Moń <desowin@gmail.com>",
"Stephen Rigler <riglersc@gmail.com>",
"Jaco Kroon <jaco@kroon.co.za>",
"Rafael Moreno <laocanfei@yahoo.com>",
"Andrew O. Shadoura <bugzilla@tut.by>",
"Andreas Karajannis <aakara13@googlemail.com>",
"Miklos Aubert <miklos.aubert@gmail.com>",
"Jonathan A. Tice <jonandtice@gmail.com>",
"John Hammen <jhammen@gmail.com>",
"Ahmed Toulan <thelinuxer@gmail.com>",
"Tim LaBerge <tlaberge@visi.com>",
NULL
};
static const gchar copyright[] = "Copyright \xc2\xa9 2009 Tomasz Moń";
static const gchar website[] = "http://desowin.org/gdigi/";
static const gchar version[] = "0.3.0";
GtkWidget *window = g_object_get_data(G_OBJECT(action), "window");
@@ -688,6 +863,10 @@ static void action_show_about_dialog_cb(GtkAction *action)
"authors", authors,
"copyright", copyright,
"website", website,
"license-type", GTK_LICENSE_GPL_3_0,
"wrap-license", TRUE,
"program-name", "gdigi",
"version", version,
NULL);
}
@@ -698,13 +877,25 @@ typedef struct {
gchar *suffix;
} SupportedFileTypes;
static SupportedFileTypes file_types[] = {
{"RP250Preset", "*.rp250p"},
{"RP500Preset", "*.rp500p"},
SupportedFileTypes file_types[] = {
[ RP150] = {"RP150Preset", "*.rp150p"},
[ RP155 ] = {"RP155Preset", "*.rp155p"},
[ RP250 ] = {"RP250Preset", "*.rp250p"},
[ RP255 ] = {"RP255Preset", "*.rp255p"},
[ RP355 ] = {"RP355Preset", "*.rp355p"},
[ RP500 ] = {"RP500Preset", "*.rp500p"},
[ RP1000 ] = {"RP1000Preset", "*.rp1000p"},
[ GNX4 ] = {"GNX4 Preset", "*.g4p"},
[ GNX3000 ] = {"GNX3kPreset", "*.g3kp"},
};
static guint n_file_types = G_N_ELEMENTS(file_types);
gchar *
get_preset_filename (int prod_id)
{
return file_types[prod_id].name;
}
#endif /* DOXYGEN_SHOULD_SKIP_THIS */
/**
@@ -736,12 +927,21 @@ static void action_open_preset_cb(GtkAction *action)
int x;
for (x=0; x<n_file_types; x++) {
GtkFileFilter *current_filter = gtk_file_filter_new();
if (file_types[x].name == NULL) {
g_message("Skipping NULL array entry");
continue;
}
gtk_file_filter_set_name(current_filter, file_types[x].name);
gtk_file_filter_add_pattern(current_filter, file_types[x].suffix);
gtk_file_filter_add_pattern(filter, file_types[x].suffix);
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), current_filter);
if (x == product_id) {
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), current_filter);
}
}
gboolean loaded = FALSE;
@@ -758,41 +958,77 @@ static void action_open_preset_cb(GtkAction *action)
gtk_widget_hide(dialog);
GString *msg = g_string_sized_new(500);
GList *iter = preset->params;
gint len = g_list_length(iter);
g_string_append_printf(msg, "%c%c",
((len & 0xFF00) >> 8),
(len & 0xFF));
while (iter) {
SettingParam *param = iter->data;
iter = iter->next;
g_string_append_printf(msg, "%c%c%c",
((param->id & 0xFF00) >> 8),
(param->id & 0xFF),
param->position);
append_value(msg, param->value);
};
GString *start = g_string_new(NULL);
g_string_append_printf(start,
"%c%c%s%c%c%c",
PRESETS_EDIT_BUFFER, 0,
preset->name, 0 /* NULL terminated string */,
0 /* modified */, 2 /* messages to follow */);
0 /* modified */,
/* messages to follow */
preset->genetxs ? 10 : 2);
send_message(RECEIVE_PRESET_START, start->str, start->len);
send_message(RECEIVE_PRESET_PARAMETERS, msg->str, msg->len);
send_preset_parameters(preset->params);
if (preset->genetxs != NULL) {
gint i;
/* GNX4 sends messages in following order:
* Section Bank Index
* 0x00 0x04 0x0000
* 0x00 0x04 0x0001
* 0x01 0x04 0x0000
* 0x01 0x04 0x0001
* 0x00 0x04 0x0002
* 0x00 0x04 0x0003
* 0x01 0x04 0x0002
* 0x01 0x04 0x0003
*/
/* GNX3000 sends messages in following order:
* Section Bank Index
* 0x07 0x04 0x0000
* 0x07 0x04 0x0001
* 0x08 0x04 0x0000
* 0x08 0x04 0x0001
* 0x07 0x04 0x0002
* 0x07 0x04 0x0003
* 0x08 0x04 0x0002
* 0x08 0x04 0x0003
*/
for (i = 0; i < 2; i++) {
GList *iter = preset->genetxs;
while (iter) {
SectionID section;
guint bank, index;
SettingGenetx *genetx = (SettingGenetx *) iter->data;
iter = iter->next;
section = get_genetx_section_id(genetx->version,
genetx->type);
bank = 0x04;
if (i == 0) {
index = genetx->channel;
} else {
if (genetx->channel == GENETX_CHANNEL1) {
index = GENETX_CHANNEL1_CUSTOM;
} else if (genetx->channel == GENETX_CHANNEL2) {
index = GENETX_CHANNEL2_CUSTOM;
}
}
send_object(section, bank, index,
genetx->name, genetx->data);
}
}
}
send_message(RECEIVE_PRESET_END, NULL, 0);
show_store_preset_window(window, preset->name);
g_string_free(start, TRUE);
g_string_free(msg, TRUE);
preset_free(preset);
loaded = TRUE;
}
@@ -803,6 +1039,57 @@ static void action_open_preset_cb(GtkAction *action)
dialog = NULL;
}
/**
* \param action the object which emitted the signal
*
* Shows file chooser dialog.
* If the user chooses a file, the preset in the edit buffer is
* written out in XML format.
**/
static void action_save_preset_cb(GtkAction *action)
{
static GtkWidget *dialog = NULL;
if (dialog != NULL)
return;
GtkWidget *window = g_object_get_data(G_OBJECT(action), "window");
dialog = gtk_file_chooser_dialog_new("Save Preset", GTK_WINDOW(window),
GTK_FILE_CHOOSER_ACTION_SAVE,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
NULL);
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
GError *error = NULL;
gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
if (error) {
show_error_message(window, error->message);
g_error_free(error);
error = NULL;
} else {
gchar real_filename[256];
GList *list = get_current_preset();
Preset *preset = create_preset_from_data(list);
snprintf(real_filename, 256, "%s.%s",
filename, file_types[product_id].suffix + 2);
gtk_widget_hide(dialog);
write_preset_to_xml(preset, real_filename);
preset_free(preset);
}
g_free(filename);
}
gtk_widget_destroy(dialog);
dialog = NULL;
}
/**
* \param list widget tree list to be freed
*
@@ -834,11 +1121,12 @@ static void action_quit_cb(GtkAction *action)
static GtkActionEntry entries[] = {
{"File", NULL, "_File"},
{"Preset", NULL, "_Preset"},
{"Help", NULL, "_Help"},
{"Open", GTK_STOCK_OPEN, "_Open", "<control>O", "Open preset file", G_CALLBACK(action_open_preset_cb)},
{"Quit", GTK_STOCK_QUIT, "_Quit", "<control>Q", "Quit", G_CALLBACK(action_quit_cb)},
{"Store", NULL, "_Store", "<control>S", "Store", G_CALLBACK(action_store_cb)},
{"Preset", NULL, "_Preset"},
{"Store", NULL, "_Store Preset to Device", "<control>D", "Store Preset to Device", G_CALLBACK(action_store_cb)},
{"Load", GTK_STOCK_OPEN, "_Load Preset from File", "<control>O", "Load Preset from File", G_CALLBACK(action_open_preset_cb)},
{"Save", GTK_STOCK_SAVE, "_Save Preset to File", "<control>S", "Save Preset to File", G_CALLBACK(action_save_preset_cb)},
{"Help", NULL, "_Help"},
{"About", GTK_STOCK_ABOUT, "_About", "<control>A", "About", G_CALLBACK(action_show_about_dialog_cb)},
};
static guint n_entries = G_N_ELEMENTS(entries);
@@ -847,12 +1135,14 @@ static const gchar *menu_info =
"<ui>"
" <menubar name='MenuBar'>"
" <menu action='File'>"
" <menuitem action='Open'/>"
" <separator/>"
" <menuitem action='Quit'/>"
" </menu>"
" <menu action='Preset'>"
" <menuitem action='Store'/>"
" <separator/>"
" <menuitem action='Load'/>"
" <menuitem action='Save'/>"
" </menu>"
" <menu action='Help'>"
" <menuitem action='About'/>"
@@ -909,8 +1199,9 @@ static void add_menubar(GtkWidget *window, GtkWidget *vbox)
FALSE, FALSE, 0);
add_action_data(ui, "/MenuBar/File/Quit", window);
add_action_data(ui, "/MenuBar/File/Open", window);
add_action_data(ui, "/MenuBar/Preset/Store", window);
add_action_data(ui, "/MenuBar/Preset/Save", window);
add_action_data(ui, "/MenuBar/Preset/Load", window);
add_action_data(ui, "/MenuBar/Help/About", window);
g_object_unref(ui);
@@ -966,7 +1257,7 @@ void gui_create(Device *device)
notebook = gtk_notebook_new();
gtk_box_pack_start(GTK_BOX(hbox), notebook, TRUE, TRUE, 2);
knob_anim = gtk_knob_animation_new_from_inline(knob_pixbuf);
knob_anim = gtk_knob_animation_new_from_inline();
widget_tree = g_tree_new_full(widget_tree_key_compare_func,
NULL, /* key compare data */
@@ -1042,9 +1333,10 @@ gboolean unsupported_device_dialog(Device **device)
"Please take a look at gdigi's HACKING file.");
gtk_container_add(GTK_CONTAINER(vbox), label);
combo_box = gtk_combo_box_new_text();
combo_box = gtk_combo_box_text_new();
for (x=0; x<n_supported_devices; x++) {
gtk_combo_box_append_text(GTK_COMBO_BOX(combo_box), supported_devices[x]->name);
gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(combo_box),
NULL, supported_devices[x]->name);
}
gtk_container_add(GTK_CONTAINER(vbox), combo_box);
@@ -1063,3 +1355,55 @@ gboolean unsupported_device_dialog(Device **device)
gtk_widget_destroy(dialog);
return FALSE;
}
/**
* \param devices List containing the available Digitech devices.
*
* Displays dialogbox for choosing a device.
*
* \return Index of the selected device or -1 on failure.
**/
gint select_device_dialog (GList *devices)
{
GtkWidget *dialog;
GtkWidget *label;
GtkWidget *combo_box;
GtkWidget *vbox;
GList *device;
dialog = gtk_dialog_new_with_buttons("Select Digitech device",
NULL, GTK_DIALOG_MODAL,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
NULL);
vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
label = gtk_label_new("You have multiple Digitech devices, select one.\n");
gtk_container_add(GTK_CONTAINER(vbox), label);
combo_box = gtk_combo_box_text_new();
device = g_list_first(devices);
do {
char *name;
snd_card_get_longname(GPOINTER_TO_INT(device->data), &name);
gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(combo_box), NULL, name);
} while ((device = g_list_next(device)));
gtk_container_add(GTK_CONTAINER(vbox), combo_box);
gtk_widget_show_all(vbox);
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
gint number = gtk_combo_box_get_active(GTK_COMBO_BOX(combo_box));
if (number != -1) {
gtk_widget_destroy(dialog);
return (number);
}
}
gtk_widget_destroy(dialog);
return -1;
}

3
gui.h
View File

@@ -17,14 +17,17 @@
#ifndef GDIGI_GUI_H
#define GDIGI_GUI_H
#include <gtk/gtk.h>
#include <glib.h>
#include "effects.h"
gchar * get_preset_filename(int prod_id);
void show_error_message(GtkWidget *parent, gchar *message);
void apply_setting_param_to_gui(SettingParam *param);
gboolean apply_current_preset_to_gui(gpointer data);
void gui_create(Device *device);
void gui_free();
gboolean unsupported_device_dialog(Device **device);
gint select_device_dialog (GList *devices);
#endif /* GDIGI_GUI_H */

5324
knob.h Normal file

File diff suppressed because it is too large Load Diff

167
preset.c
View File

@@ -29,12 +29,24 @@ enum {
PARSER_TYPE_PARAM_POSITION,
PARSER_TYPE_PARAM_VALUE,
PARSER_TYPE_PARAM_NAME,
PARSER_TYPE_PARAM_TEXT
PARSER_TYPE_PARAM_TEXT,
PARSER_TYPE_GENETX_VERSION,
PARSER_TYPE_GENETX_TYPE,
PARSER_TYPE_GENETX_CHANNEL,
PARSER_TYPE_GENETX_NAME,
PARSER_TYPE_GENETX_DATA
};
enum {
SECTION_NOT_SET = -1,
SECTION_PARAMS,
SECTION_GENETX
};
typedef struct {
int depth;
int id;
int section;
Preset *preset;
} AppData;
@@ -46,11 +58,16 @@ static void XMLCALL start(void *data, const char *el, const char **attr) {
if (ad->depth == 1) {
ad->id = PARSER_TYPE_PRESET_NAME;
} else if (ad->depth == 3) {
ad->id = PARSER_TYPE_PARAM_NAME;
if (ad->section == SECTION_PARAMS) {
ad->id = PARSER_TYPE_PARAM_NAME;
} else if (ad->section == SECTION_GENETX) {
ad->id = PARSER_TYPE_GENETX_NAME;
}
}
}
if (g_strcmp0(el, "Params") == 0) {
ad->section = SECTION_PARAMS;
if (ad->preset->params != NULL)
g_message("Params aleady exists!");
} else if (g_strcmp0(el, "Param") == 0) {
@@ -64,6 +81,21 @@ static void XMLCALL start(void *data, const char *el, const char **attr) {
ad->id = PARSER_TYPE_PARAM_VALUE;
} else if (g_strcmp0(el, "Text") == 0) {
ad->id = PARSER_TYPE_PARAM_TEXT;
} else if (g_strcmp0(el, "Genetx") == 0) {
ad->section = SECTION_GENETX;
if (ad->preset->genetxs != NULL)
g_message("Genetx already exists!");
} else if (g_strcmp0(el, "GenetxModel") == 0) {
SettingGenetx *genetx = setting_genetx_new();
ad->preset->genetxs = g_list_prepend(ad->preset->genetxs, genetx);
} else if (g_strcmp0(el, "Version") == 0) {
ad->id = PARSER_TYPE_GENETX_VERSION;
} else if (g_strcmp0(el, "Type") == 0) {
ad->id = PARSER_TYPE_GENETX_TYPE;
} else if (g_strcmp0(el, "Channel") == 0) {
ad->id = PARSER_TYPE_GENETX_CHANNEL;
} else if (g_strcmp0(el, "Data") == 0) {
ad->id = PARSER_TYPE_GENETX_DATA;
}
ad->depth++;
@@ -88,28 +120,87 @@ static void XMLCALL text_cb(void *data, const char* text, int len)
ad->preset->name = g_strndup(text, len);
}
if (ad->preset->params == NULL)
return;
if (ad->section == SECTION_PARAMS) {
if (ad->preset->params == NULL)
return;
SettingParam *param = (SettingParam *) ad->preset->params->data;
if (param == NULL)
return;
SettingParam *param = (SettingParam *) ad->preset->params->data;
if (param == NULL)
return;
gchar *value = g_strndup(text, len);
gchar *value = g_strndup(text, len);
switch (ad->id) {
case PARSER_TYPE_PARAM_ID:
param->id = atoi(value);
break;
case PARSER_TYPE_PARAM_POSITION:
param->position = atoi(value);
break;
case PARSER_TYPE_PARAM_VALUE:
param->value = atoi(value);
break;
switch (ad->id) {
case PARSER_TYPE_PARAM_ID:
param->id = atoi(value);
break;
case PARSER_TYPE_PARAM_POSITION:
param->position = atoi(value);
break;
case PARSER_TYPE_PARAM_VALUE:
param->value = atoi(value);
break;
}
g_free(value);
} else if (ad->section == SECTION_GENETX) {
if (ad->preset->genetxs == NULL)
return;
SettingGenetx *genetx = (SettingGenetx *) ad->preset->genetxs->data;
if (genetx == NULL)
return;
gchar *value = g_strndup(text, len);
switch (ad->id) {
case PARSER_TYPE_GENETX_VERSION:
if (g_strcmp0(value, "Version1") == 0) {
genetx->version = GENETX_VERSION_1;
} else if (g_strcmp0(value, "Version2") == 0) {
genetx->version = GENETX_VERSION_2;
} else {
g_message("Unknown GeNetX version: %s", value);
}
break;
case PARSER_TYPE_GENETX_TYPE:
if (g_strcmp0(value, "Amp") == 0) {
genetx->type = GENETX_TYPE_AMP;
} else if (g_strcmp0(value, "Cabinet") == 0) {
genetx->type = GENETX_TYPE_CABINET;
} else {
g_message("Unknown GeNetX type: %s", value);
}
break;
case PARSER_TYPE_GENETX_CHANNEL:
if (g_strcmp0(value, "Channel1") == 0) {
genetx->channel = GENETX_CHANNEL1;
} else if (g_strcmp0(value, "Channel2") == 0) {
genetx->channel = GENETX_CHANNEL2;
} else {
g_message("Unknown GeNetX channel: %s", value);
}
break;
case PARSER_TYPE_GENETX_NAME:
/* reassign pointer */
genetx->name = value;
value = NULL;
break;
case PARSER_TYPE_GENETX_DATA:
{
guchar *data = NULL;
gsize length = 0;
data = g_base64_decode(value, &length);
genetx->data = g_string_new_len((gchar *) data, length);
g_free(data);
break;
}
}
g_free(value);
}
g_free(value);
}
#endif /* DOXYGEN_SHOULD_SKIP_THIS */
@@ -124,16 +215,13 @@ static void XMLCALL text_cb(void *data, const char* text, int len)
**/
Preset *create_preset_from_xml_file(gchar *filename, GError **error)
{
GFile *file;
GError *err = NULL;
gchar *contents;
file = g_file_new_for_path(filename);
if (g_file_get_contents(filename, &contents, NULL, &err) == FALSE) {
g_message("Failed to get %s contents: %s", filename, err->message);
*error = g_error_copy(err);
g_error_free(err);
g_object_unref(file);
return NULL;
}
@@ -142,6 +230,7 @@ Preset *create_preset_from_xml_file(gchar *filename, GError **error)
ad->preset = g_slice_new(Preset);
ad->preset->name = NULL;
ad->preset->params = NULL;
ad->preset->genetxs = NULL;
ad->id = PARSER_TYPE_NOT_SET;
XML_Parser p;
@@ -157,21 +246,35 @@ Preset *create_preset_from_xml_file(gchar *filename, GError **error)
preset_free(ad->preset);
g_slice_free(AppData, ad);
g_free(contents);
g_object_unref(file);
return NULL;
}
Preset *preset = ad->preset;
preset->params = g_list_reverse(preset->params);
preset->genetxs = g_list_reverse(preset->genetxs);
XML_ParserFree(p);
g_slice_free(AppData, ad);
g_free(contents);
g_object_unref(file);
return preset;
}
gint params_cmp(gconstpointer a, gconstpointer b)
{
const SettingParam *param_a = a;
const SettingParam *param_b = b;
if (param_a->position != param_b->position) {
return (param_a->position > param_b->position) ? 1 : -1;
}
if (param_a->id != param_b->id) {
return (param_a->id > param_b->id) ? 1 : -1;
}
return 0;
}
/**
* \param list list containing unpacked preset SysEx messages.
*
@@ -192,6 +295,7 @@ Preset *create_preset_from_data(GList *list)
Preset *preset = g_slice_new(Preset);
preset->name = NULL;
preset->params = NULL;
preset->genetxs = NULL;
iter = list;
for (iter = list; iter; iter = g_list_next(iter)) {
@@ -225,7 +329,7 @@ Preset *create_preset_from_data(GList *list)
g_message("%d ID %d Position %d Value %d", n, param->id, param->position, param->value);
} while ((x < data->len) && n<total);
g_message("TOTAL %d", total);
preset->params = g_list_reverse(preset->params);
preset->params = g_list_sort(preset->params, params_cmp);
break;
case RECEIVE_PRESET_END:
break;
@@ -254,8 +358,15 @@ void preset_free(Preset *preset)
g_list_free(preset->params);
}
if (preset->name != NULL)
g_free(preset->name);
if (preset->genetxs != NULL) {
GList *iter;
for (iter = preset->genetxs; iter; iter = iter->next) {
setting_genetx_free((SettingGenetx*)iter->data);
}
g_list_free(preset->genetxs);
}
g_free(preset->name);
g_slice_free(Preset, preset);
}

View File

@@ -22,10 +22,11 @@
typedef struct {
gchar *name;
GList *params;
GList *genetxs;
} Preset;
Preset *create_preset_from_xml_file(gchar *filename, GError **error);
Preset *create_preset_from_data(GList *list);
void preset_free(Preset *preset);
void write_preset_to_xml(Preset *preset, gchar *filename);
#endif /* GDIGI_PRESET_H */

262
preset_xml.c Normal file
View File

@@ -0,0 +1,262 @@
/*
* Copyright (c) 2009 Tomasz Moń <desowin@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; under version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses>.
*/
#include <gio/gio.h>
#include <expat.h>
#include <libxml/encoding.h>
#include <libxml/xmlreader.h>
#include <libxml/xmlwriter.h>
#include <string.h>
#include "preset.h"
#include "gdigi.h"
#include "gui.h"
#include "gdigi_xml.h"
#ifndef DOXYGEN_SHOULD_SKIP_THIS
// FIXME: Bring the xml settings into this module?
extern XmlSettings xml_settings[];
extern guint n_xml_settings;
extern EffectValues values_on_off;
/**
* \param id modifier ID
* \param position modifier position
*
* Gets modifier info.
*
* \return Modifier which must not be freed, or NULL if no matching Modifier has been found.
*/
XmlSettings *get_xml_settings (guint id, guint position)
{
gint x;
for (x=0; x< n_xml_settings; x++) {
if ((xml_settings[x].id == id) && (xml_settings[x].position == position)) {
return xml_settings + x;
}
}
return NULL;
}
gchar *
map_xml_value(XmlSettings *xml, gint value)
{
switch (xml->values->type) {
case VALUE_TYPE_LABEL:
if ((xml->values == &values_on_off) && (value > 1)) {
g_message("Skipping modifier->label %s\n", xml->label);
return NULL;
}
if (value > xml->values->max || value < xml->values->min) {
g_message("%s value %d out of range %0.1f %0.1f", xml->label, value, xml->values->min, xml->values->max);
}
{
XmlLabel *labels = xml->xml_labels;
guint labels_amt = xml->xml_labels_amt;
gint i;
// Maybe this can be a computation: i = xml->values->min + val
for (i = 0; i < labels_amt; i++) {
if (labels[i].type == value) {
return (labels[i].label);
}
}
}
break;
default:
break;
}
return NULL;
}
gboolean value_is_extra (EffectValues *val, SettingParam *param)
{
if ((param->value < val->min) || (param->value > val->max)) {
return TRUE;
}
return FALSE;
}
#define GDIGI_ENCODING "utf-8"
void
write_preset_to_xml(Preset *preset, gchar *filename)
{
int rc;
xmlTextWriterPtr writer;
GList *iter_params = preset->params;
guint last_id = 0;
guint last_position = 0;
printf("Creating a new xml doc\n");
/* Create a new XmlWriter for uri, with no compression. */
writer = xmlNewTextWriterFilename(filename, 0);
if (writer == NULL) {
printf("testXmlwriterFilename: Error creating the xml writer\n");
return;
}
/*
* Start the document with the xml default for the version,
* encoding and the default for the standalone declaration.
*/
rc = xmlTextWriterStartDocument(writer, NULL, GDIGI_ENCODING, NULL);
if (rc < 0) {
printf
("testXmlwriterFilename: Error at xmlTextWriterStartDocument\n");
return;
}
rc = xmlTextWriterSetIndent(writer, 1);
rc = xmlTextWriterSetIndentString(writer, BAD_CAST " ");
/* Write the tag identifying type of prefix, schema version and ns. */
rc = xmlTextWriterStartElement(writer, BAD_CAST get_preset_filename(product_id));
rc = xmlTextWriterWriteAttribute(writer, BAD_CAST "SchemaVersion",
BAD_CAST "1.2");
rc = xmlTextWriterWriteAttribute(writer, BAD_CAST "xmlns",
BAD_CAST "http://www.digitech.com/xml/preset");
/* Write the Name tag. */
rc = xmlTextWriterWriteElement(writer, BAD_CAST "Name", BAD_CAST preset->name);
rc = xmlTextWriterStartElement(writer, BAD_CAST "Params");
while (iter_params) {
XmlSettings *xml;
SettingParam *param = (SettingParam *) iter_params->data;
if (param->id == last_id && param->position == last_position) {
g_message("Skipping duplicate parameter id %d position %d",
last_id, last_position);
iter_params = iter_params->next;
continue;
}
rc = xmlTextWriterStartElement(writer, BAD_CAST "Param");
rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "ID",
"%d", param->id);
rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "Position",
"%d", param->position);
rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "Value",
"%d", param->value);
last_id = param->id;
last_position = param->position;
xml = get_xml_settings(param->id, param->position);
if (!xml) {
printf("Failed to get xml settings for id %d position %d\n",
param->id, param->position);
} else {
ValueType type;
gchar *suffix = "";
gdouble step = 1.0;
gint offset = 0;
gboolean decimal = FALSE;
EffectValues *values = NULL;
rc = xmlTextWriterWriteElement(writer, BAD_CAST "Name",
BAD_CAST xml->label);
values = xml->values;
type = values->type;
while ((type & VALUE_TYPE_EXTRA) && value_is_extra(values, param)) {
values = values->extra;
type = values->type;
}
type &= ~VALUE_TYPE_EXTRA;
if (type & VALUE_TYPE_OFFSET) {
offset = values->offset;
type &= ~VALUE_TYPE_OFFSET;
}
if (type & VALUE_TYPE_STEP) {
step = values->step;
type &= ~VALUE_TYPE_STEP;
}
if (type & VALUE_TYPE_SUFFIX) {
suffix = values->suffix;
type &= ~VALUE_TYPE_SUFFIX;
}
if (type & VALUE_TYPE_DECIMAL) {
decimal = TRUE;
type &= ~VALUE_TYPE_DECIMAL;
}
switch (type) {
case VALUE_TYPE_LABEL:
{
char *textp = map_xml_value(xml, param->value);
if (!textp) {
g_message("Unable to map %s value %d for id %d position %d",
xml->label, param->value, param->id, param->position);
textp = "";
}
rc = xmlTextWriterWriteElement(writer, BAD_CAST "Text",
BAD_CAST textp);
break;
}
case VALUE_TYPE_PLAIN:
{
if (decimal) {
double value = (param->value + offset) * step;
rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "Text",
"%0.2f%s", value, suffix);
} else {
gint value = (param->value + offset) * step;
rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "Text",
"%d%s", value, suffix);
}
break;
}
case VALUE_TYPE_NONE:
rc = xmlTextWriterStartElement(writer, BAD_CAST "Text");
rc = xmlTextWriterEndElement(writer);
break;
default:
g_message("Unhandled value type %d", type);
break;
}
}
rc = xmlTextWriterEndElement(writer);
iter_params = iter_params->next;
}
/* Here we could close the elements ORDER and EXAMPLE using the
* function xmlTextWriterEndElement, but since we do not want to
* write any other elements, we simply call xmlTextWriterEndDocument,
* which will do all the work. */
rc = xmlTextWriterEndDocument(writer);
if (rc < 0) {
printf("testXmlwriterFilename: Error at xmlTextWriterEndDocument\n");
return;
}
xmlFreeTextWriter(writer);
}
#endif /* DOXYGEN_SHOULD_SKIP_THIS */