33 Commits
0.1.4 ... 0.1.6

Author SHA1 Message Date
Tomasz Moń
991527418a Don't use deprecated functions in GtkKnob; optipng knob.png 2009-03-24 22:21:01 +01:00
Tomasz Moń
96317d5ac5 actually 'is it connected' doesn't make sense here 2009-03-17 15:36:16 +01:00
Tomasz Moń
d24cc97f5d add error reporting to create_preset_from_xml_file 2009-03-17 12:54:46 +01:00
Tomasz Moń
cac7e74d71 display preset number in preset list 2009-03-15 20:12:58 +01:00
Tomasz Moń
74583e9a7c make GUI more compact 2009-03-15 20:06:18 +01:00
Tomasz Moń
03c3c48ecf remove reduntant text 2009-03-15 18:52:14 +01:00
Tomasz Moń
aa2cc7bbb1 some Doxygen work 2009-03-14 18:51:08 +01:00
Tomasz Moń
ec14a41869 Added tag 0.1.5 for changeset 45f897c02e72 2009-03-14 14:27:12 +01:00
Tomasz Moń
9778dc47c3 add comments 2009-03-14 14:23:18 +01:00
Tomasz Moń
71c3a1e66f use g_slice 2009-03-14 12:58:11 +01:00
Tomasz Moń
bdb35ddf54 finish modifier_linkable_list(), add modifier_group_free() 2009-03-14 12:39:45 +01:00
Tomasz Moń
4ca140ab48 rename get_modifier_label to get_modifier 2009-03-14 10:31:40 +01:00
Tomasz Moń
d2f821eee1 add EffectValues to Modifiers 2009-03-14 10:24:58 +01:00
Tomasz Moń
e607c6cf37 move modifier_linkable_list to effects.c 2009-03-14 10:07:00 +01:00
Tomasz Moń
dc66db841a introduce get_message_by_id 2009-03-14 10:04:56 +01:00
Tomasz Moń
e80683aea2 add cabinet models 2009-03-14 08:38:28 +01:00
Tomasz Moń
b7d2d7d1c5 introduce EffectValues 2009-03-13 22:42:46 +01:00
Tomasz Moń
cd5d7387f3 remove obsolete function 2009-03-13 20:33:24 +01:00
Tomasz Moń
8a39a178c6 add tone library and effects library 2009-03-13 19:46:02 +01:00
Tomasz Moń
40ebcfaf16 make modifiers[] complete 2009-03-13 18:00:41 +01:00
Tomasz Moń
de68d0449f some renames, no functional changes 2009-03-10 20:57:34 +01:00
Tomasz Moń
d280bf1ca6 add some comments 2009-03-10 19:45:16 +01:00
Tomasz Moń
b245e1bac6 add gtk_knob_key_press 2009-03-09 22:51:38 +01:00
Tomasz Moń
8d7fffb6d1 atan2f requires C99 2009-03-09 20:58:03 +01:00
Tomasz Moń
f8e35ff685 let GtkKnob grab focus 2009-03-09 19:57:13 +01:00
Tomasz Moń
3f717a51cd add gui_free 2009-03-09 18:49:29 +01:00
Tomasz Moń
90df427200 add knobs 2009-03-09 18:22:20 +01:00
Tomasz Moń
322c59032e tweak GtkKnob a bit 2009-03-08 13:46:14 +01:00
Tomasz Moń
1b1919649e add GtkKnob from phasex-0.11.1 2009-03-08 13:31:43 +01:00
Tomasz Moń
e7c7efe857 expression pedal settings work-in-progress 2009-03-08 13:25:37 +01:00
Tomasz Moń
17b067f853 don't crash if query preset names fail 2009-03-04 19:00:32 +01:00
Tomasz Moń
8ec3d6579c change query_preset_names 2009-03-04 18:26:22 +01:00
Tomasz Moń
5bd6f8fc2c Added tag 0.1.4 for changeset 78367821f4f4 2009-03-04 08:14:21 +01:00
14 changed files with 2527 additions and 558 deletions

View File

@@ -1,5 +1,5 @@
CC = gcc
CFLAGS = `pkg-config --cflags glib-2.0 gio-2.0 gtk+-2.0` -Wall -g -ansi
CFLAGS = `pkg-config --cflags glib-2.0 gio-2.0 gtk+-2.0` -Wall -g -ansi -std=c99
OFLAG = -o
LIBS = `pkg-config --libs glib-2.0 gio-2.0 gtk+-2.0 alsa` -lexpat
@@ -9,8 +9,8 @@ LIBS = `pkg-config --libs glib-2.0 gio-2.0 gtk+-2.0 alsa` -lexpat
all: gdigi
gdigi: gdigi.o tests.o gui.o effects.o preset.o
$(CC) $(LIBS) $(OFLAG) gdigi gdigi.o tests.o gui.o effects.o preset.o
gdigi: knob.h gdigi.o tests.o gui.o effects.o preset.o gtkknob.o
$(CC) $(LIBS) $(OFLAG) gdigi gdigi.o tests.o gui.o effects.o preset.o gtkknob.o
gdigi.o: gdigi.c
@@ -22,6 +22,13 @@ effects.o: effects.c
preset.o: preset.c
gtkknob.o: gtkknob.c
gtkknob.o: gtkknob.c
knob.h:
gdk-pixbuf-csource --name=knob_pixbuf knob.png > $@
clean:
rm *.o

8
TODO
View File

@@ -1,8 +1,6 @@
-make complete gui
-cabinet models
-tone library
-effects library
-effects level
-handling presets (saving, exporting to xml patches)
-buildsystem
-expression pedal settings
-buildsystem (install knob.png to share dir, don't use inline knob pixbuf)
-add expression pedal settings to gui
-read asynchronously from MIDI IN

932
effects.c

File diff suppressed because it is too large Load Diff

View File

@@ -20,33 +20,47 @@
#include <glib/gtypes.h>
typedef struct {
char *label; /* Parameter name */
gdouble min; /* Parameter minumum value */
gdouble max; /* Parameter maximum value */
guint option; /* ID (to set parameter) */
guint position; /* position */
gdouble min; /**< Minumum value */
gdouble max; /**< Maximum value */
GStrv labels; /**< value labels */
} EffectValues;
typedef struct {
gchar *label; /**< Parameter name */
guint id; /**< ID (to set parameter) */
guint position; /**< position */
EffectValues *values; /**< valid parameter values */
} EffectSettings;
typedef struct {
guint id; /* value (type) */
gchar *label; /* Effect name */
guint option; /* ID (to set effect type) */
guint position; /* position */
EffectSettings *settings; /* possible parameters */
gint settings_amt; /* possible parameters length */
guint type; /**< value (type) */
gchar *label; /**< Effect name */
guint id; /**< ID (to set effect type) */
guint position; /**< position */
EffectSettings *settings; /**< possible parameters */
gint settings_amt; /**< possible parameters length */
} EffectGroup;
typedef struct {
char *label; /* Base effect name */
guint option; /* ID (to set effect on/off) */
guint position; /* position */
EffectGroup *group; /* possible effect types */
gint group_amt; /* possible effect types length */
gchar *label; /**< Group label */
guint id; /**< ID to set effect on/off, or if it isn't on/off group then -1 */
guint position; /**< position */
EffectGroup *group; /**< possible effect types */
gint group_amt; /**< possible effect types length */
} Effect;
typedef struct {
Effect *effect; /* list of supported effects */
gint amt; /* list of supported effects length */
gchar *label; /**< base effect name */
Effect *effect; /**< list of supported effects */
gint amt; /**< list of supported effects length */
} EffectList;
typedef struct {
EffectGroup *group;
gint group_amt;
} ModifierGroup;
ModifierGroup *modifier_linkable_list();
void modifier_group_free(ModifierGroup *modifier_group);
#endif /* GDIGI_EFFECTS_H */

436
gdigi.c
View File

@@ -30,28 +30,15 @@ static snd_rawmidi_t *output = NULL;
static snd_rawmidi_t *input = NULL;
static char *device = "hw:1,0,0";
/*
calculate checksum
array - the command to set over usb
length - length of array
check - position of checksum byte in array
*/
char calculate_checksum(gchar *array, int length, int check)
{
int x;
char checksum;
checksum = 0x07;
for (x = 0; x<length; x++) {
if (x == check) continue;
checksum ^= array[x];
}
return checksum;
}
static char calc_checksum(gchar *array, gint length)
/**
* \param array data to calculate checksum
* \param length data length
*
* Calculates message checksum.
*
* \return calculated checksum.
**/
static char calculate_checksum(gchar *array, gint length)
{
int x;
int checksum = 0;
@@ -63,10 +50,11 @@ static char calc_checksum(gchar *array, gint length)
return checksum;
}
/*
opens MIDI device
Returns TRUE on error
*/
/**
* Opens MIDI device. This function modifies global input and output variables.
*
* \return FALSE on success, TRUE on error.
**/
gboolean open_device()
{
int err;
@@ -88,19 +76,103 @@ gboolean open_device()
return FALSE;
}
/**
* \param data data to be sent
* \param length data length
*
* Sends data to device. This function uses global output variable.
**/
void send_data(char *data, int length)
{
if (output == NULL)
open_device();
snd_rawmidi_write(output, data, length);
}
/*
reads data from MIDI IN
returns GString containing data
if no data was read it returns NULL
*/
/**
* \param data data to be packed
* \param len data length
*
* Packs data using method used on all newer DigiTech products.
*
* \return GString containing packed data
**/
GString *pack_data(gchar *data, gint len)
{
GString *packed;
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++) {
if ((i % 7) == 0) {
packed->str[status_byte] = status;
status = 0;
status_byte = packed->len;
g_string_append_c(packed, '\0');
}
g_string_append_c(packed, (data[i] & 0x7F));
status |= (data[i] & 0x80) >> ((i%7) + 1);
}
packed->str[status_byte] = status;
return packed;
}
/**
* \param msg message to unpack
*
* Unpacks message data. This function modifies given GString.
**/
static void unpack_message(GString *msg)
{
int offset;
int x;
int i;
unsigned char status;
unsigned char *str;
gboolean stop = FALSE;
g_return_if_fail(msg != NULL);
g_return_if_fail(msg->len > 9);
offset = 1;
x = 0;
i = 8;
str = (unsigned char*)msg->str;
do {
offset += 8;
status = str[offset-1];
for (x=0; x<7; x++) {
if (offset+x >= msg->len) {
stop = TRUE;
break;
}
if (str[offset+x] == 0xF7) {
str[i] = 0xF7;
stop = TRUE;
}
str[i] = (((status << (x+1)) & 0x80) | str[x+offset]);
i++;
}
} while (!stop && (offset+x < msg->len));
g_string_truncate(msg, i);
}
/**
* Reads data from MIDI IN. This function uses global input variable.
*
* \return GString containing data, or NULL when no data was read.
**/
GString* read_data()
{
/* This is mostly taken straight from alsa-utils-1.0.19 amidi/amidi.c
@@ -164,49 +236,13 @@ GString* read_data()
return string;
}
static void clear_midi_in_buffer()
{
GString *str;
do {
str = read_data();
} while (str != NULL);
}
GString *pack_data(gchar *data, gint len)
{
GString *packed;
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++) {
if ((i % 7) == 0) {
packed->str[status_byte] = status;
status = 0;
status_byte = packed->len;
g_string_append_c(packed, '\0');
}
g_string_append_c(packed, (data[i] & 0x7F));
status |= (data[i] & 0x80) >> ((i%7) + 1);
}
packed->str[status_byte] = status;
return packed;
}
/*
data - unpacked data to send
len - data length
*/
/**
* \param procedure procedure ID
* \param data unpacked message data
* \param len data length
*
* Creates SysEx message then sends it. This function uses folowing global variables: device_id, family_id and product_id.
**/
void send_message(gint procedure, gchar *data, gint len)
{
GString *msg = g_string_new_len("\xF0" /* SysEx status byte */
@@ -225,21 +261,59 @@ void send_message(gint procedure, gchar *data, gint len)
}
g_string_append_printf(msg, "%c\xF7",
calc_checksum(&msg->str[1], msg->len - 1));
calculate_checksum(&msg->str[1], msg->len - 1));
send_data(msg->str, msg->len);
g_string_free(msg, TRUE);
}
static gint get_message_id(GString *msg)
/**
* \param msg SysEx message
*
* Checks message ID.
*
* \return MessageID, or -1 on error.
**/
static MessageID get_message_id(GString *msg)
{
/** \todo check if msg is valid SysEx message */
g_return_val_if_fail(msg != NULL, -1);
if (msg->len > 7) {
return (unsigned char)msg->str[7];
}
return -1;
}
/**
* \param id MessageID of requested message
*
* Reads data from MIDI IN until message with matching id is found.
*
* \return GString containing unpacked message.
**/
GString *get_message_by_id(MessageID id)
{
GString *data = NULL;
do {
if (data)
g_string_free(data, TRUE);
data = read_data();
} while (get_message_id(data) != id);
unpack_message(data);
return data;
}
/**
* \param msg message to append value
* \param value value to append
*
* Packs value using scheme used on all newer DigiTech products.
**/
void append_value(GString *msg, guint value)
{
/* check how many bytes long the value is */
@@ -267,11 +341,13 @@ void append_value(GString *msg, guint value)
}
}
/*
id - ID as found in preset file
position - Position as found in preset file
value - Value as found in preset file
*/
/**
* \param id Parameter ID
* \param position Parameter position
* \param value Parameter value
*
* Forms SysEx message to set parameter then sends it to device.
**/
void set_option(guint id, guint position, guint value)
{
GString *msg = g_string_sized_new(9);
@@ -283,7 +359,12 @@ void set_option(guint id, guint position, guint value)
g_string_free(msg, TRUE);
}
/* x = 0 to 60 */
/**
* \param bank preset bank
* \param x preset index
*
* Switches to selected preset.
**/
void switch_preset(guint bank, guint x)
{
GString *msg = g_string_sized_new(6);
@@ -296,12 +377,12 @@ void switch_preset(guint bank, guint x)
g_string_free(msg, TRUE);
}
/* level = 0 to 99 */
void set_preset_level(int level)
{
set_option(PRESET_LEVEL, PRESET_POSITION, level);
}
/**
* \param x preset index
* \param name preset name
*
* Stores current edit buffer in user presets bank.
**/
void store_preset_name(int x, const gchar *name)
{
GString *msg = g_string_sized_new(6);
@@ -314,7 +395,12 @@ void store_preset_name(int x, const gchar *name)
g_string_free(msg, TRUE);
}
/* x = 0 to 59 (preset number) */
/**
* \param x preset index
* \param name preset name
*
* Sets preset name.
**/
void set_preset_name(int x, gchar *name)
{
GString *msg = g_string_sized_new(12);
@@ -326,12 +412,13 @@ void set_preset_name(int x, gchar *name)
g_string_free(msg, TRUE);
}
/*
Queries user preset names
Valid bank values are PRESETS_SYSTEM and PRESETS_USER
Returns GStrv which must be freed with g_strfreev
Returns NULL on error
*/
/**
* \param bank preset bank
*
* Queries preset names.
*
* \return GStrv which must be freed with g_strfreev, or NULL on error.
**/
GStrv query_preset_names(gchar bank)
{
GString *data = NULL;
@@ -340,111 +427,58 @@ GStrv query_preset_names(gchar bank)
int n_total; /* total number of presets */
gchar **str_array = NULL;
/* clear MIDI IN buffer */
clear_midi_in_buffer();
/* query user preset names */
send_message(REQUEST_PRESET_NAMES, &bank, 1);
/* read reply */
do {
if (data)
g_string_free(data, TRUE);
data = read_data();
} while (get_message_id(data) != RECEIVE_PRESET_NAMES);
data = get_message_by_id(RECEIVE_PRESET_NAMES);
if (data != NULL) {
char preset_reply_magic[] = {0xF0, 0x00, 0x00, 0x10, 0x00, 0x5E, 0x02, 0x22, 0x00, 0x01};
if (strncmp(data->str, preset_reply_magic, sizeof(preset_reply_magic)) == 0) {
char buf[10]; /* temporary, used to read single preset name */
int b = 0; /* used to iterate over buf */
if (data->len >= 10) {
n_total = data->str[9];
str_array = g_new(gchar*, n_total + 1);
str_array[n_total] = NULL;
}
if (data->len >= 10) {
n_total = data->str[10];
str_array = g_new(gchar*, n_total + 1);
str_array[n_total] = NULL;
}
for (x=10; ((x<data->len) && (n<n_total)); x++) {
if (data->str[x] == 0xF7) /* every message ends with 0xF7 */
break;
for (x=11; ((x<data->len) && (n<n_total)); x++) {
if ((x % 8) == 0) /* every 8th byte is 0x00 */
continue;
if (data->str[x] == 0xF7) /* every message ends with 0xF7 */
break;
if (data->str[x] == 0) { /* presets are splitted with 0x00 */
gchar *name;
name = g_new(gchar, b+1);
strncpy(name, buf, b);
name[b] = 0;
if (n < n_total)
str_array[n] = name;
b = 0;
n++;
} else {
if (b < 10) {
buf[b] = data->str[x];
b++;
} else {
g_message("Preset name seems to be longer than 10 chars!");
}
}
}
str_array[n] = g_strdup(&data->str[x]);
x += strlen(str_array[n]);
n++;
}
g_string_free(data, TRUE);
}
return str_array;
}
static void unpack_message(GString *msg)
{
int offset;
int x;
int i;
unsigned char status;
gboolean finish = FALSE;
offset = 9;
x = 0;
i = 8;
do {
status = (unsigned char)msg->str[offset-1];
for (x=0; x<7; x++) {
if ((unsigned char)msg->str[offset+x] == 0xF7) {
msg->str[i] = 0xF7;
finish = TRUE;
}
msg->str[i] = (((status << (x+1)) & 0x80) | (unsigned char)msg->str[x+offset]);
i++;
}
offset += 8;
} while (finish == FALSE && offset < msg->len);
}
/**
* Queries current edit buffer.
*
* \return GString containing RECEIVE_PRESET_PARAMETERS SysEx message.
**/
GString *get_current_preset()
{
GString *data = NULL;
/* clear MIDI IN buffer */
clear_midi_in_buffer();
send_message(REQUEST_PRESET, "\x04\x00", 3);
/* read reply */
data = read_data();
g_string_free(data, TRUE);
data = read_data();
unpack_message(data);
data = get_message_by_id(RECEIVE_PRESET_PARAMETERS);
return data;
}
/**
* \param device_id Variable to hold device ID
* \param family_id Variable to hold family ID
* \param product_id Variable to hold product ID
*
* Requests device information.
*
* \return TRUE on success, FALSE on error.
**/
static gboolean request_who_am_i(unsigned char *device_id, unsigned char *family_id,
unsigned char *product_id)
{
@@ -464,11 +498,52 @@ static gboolean request_who_am_i(unsigned char *device_id, unsigned char *family
return FALSE;
}
static void request_device_configuration()
{
gint os_major, os_minor;
gint cpu_major, cpu_minor;
gint protocol_version;
gint current_bank, current_preset;
gint media_card;
send_message(REQUEST_DEVICE_CONFIGURATION, NULL, 0);
GString *data = get_message_by_id(RECEIVE_DEVICE_CONFIGURATION);
if (data->len > 14) {
os_major = data->str[8];
os_minor = (((data->str[9] & 0xF0) >> 4) * 10) |
(data->str[9] & 0x0F);
g_message("OS version: %d.%d", os_major, os_minor);
cpu_major = data->str[10];
cpu_minor = data->str[11];
g_message("CPU version: %d.%d", cpu_major, cpu_minor);
protocol_version = data->str[12];
g_message("Protocol version: %d", protocol_version);
current_bank = data->str[13];
current_preset = data->str[14];
g_message("Active bank: %d", current_bank);
g_message("Active preset: %d", current_preset);
if (os_major >= 1) {
media_card = data->str[15];
g_message("Media card present: %d", media_card);
}
}
}
#ifndef DOXYGEN_SHOULD_SKIP_THIS
static GOptionEntry options[] = {
{"device", 'd', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_STRING, &device, "MIDI device port to use", NULL},
{NULL}
};
#endif /* DOXYGEN_SHOULD_SKIP_THIS */
int main(int argc, char *argv[]) {
GError *error = NULL;
GOptionContext *context;
@@ -489,10 +564,11 @@ int main(int argc, char *argv[]) {
show_error_message(NULL, "Failed to open MIDI device");
} else {
if (request_who_am_i(&device_id, &family_id, &product_id) == FALSE) {
show_error_message(NULL, "No suitable reply from device - is it connected?");
show_error_message(NULL, "No suitable reply from device");
} else {
create_window();
gui_create();
gtk_main();
gui_free();
}
}

110
gdigi.h
View File

@@ -20,6 +20,8 @@
#include <glib.h>
#include <glib-object.h>
#ifndef DOXYGEN_SHOULD_SKIP_THIS
enum {
WAH_TYPE_CRY = 132,
WAH_TYPE_FULLRANGE = 133,
@@ -164,6 +166,26 @@ enum {
#define AMP_GAIN 2497
#define AMP_LEVEL 2498
#define AMP_CAB_TYPE 2560
#define AMP_CAB_POSITION 9
/* valid AMP_CAB_TYPE values */
#define AMP_CAB_DIRECT 570
#define AMP_CAB_CHAMP 571
#define AMP_CAB_DELUXE 572
#define AMP_CAB_TWIN 576
#define AMP_CAB_BRITISH2_12 613
#define AMP_CAB_BASSMAN 579
#define AMP_CAB_BRITISH4_12 614
#define AMP_CAB_BRITISH_GREEN 616
#define AMP_CAB_VINTAGE 622
#define AMP_CAB_DIGI_SOLO 609
#define AMP_CAB_DIGI_BRIGHT 611
#define AMP_CAB_DIGI_METAL 618
#define AMP_CAB_DIGI_ROCK 619
#define AMP_CAB_DIGI_ALT 620
#define AMP_CAB_DIGI_VNTG 621
#define EQ_TYPE 3202
#define EQ_ON_OFF 3212
#define EQ_POSITION 24
@@ -348,7 +370,7 @@ enum {
#define IPS_BB 0x06
#define IPS_B 0x07
#define IPS_C 0x08
#define IPS_DD 0x09
#define IPS_DB 0x09
#define IPS_D 0x0A
#define IPS_EB 0x0B
@@ -442,11 +464,86 @@ enum {
#define EXP_MIN 8195
#define EXP_MAX 8196
#define LIBRARY_TONE 8704
#define LIBRARY_EFFECTS 8705
#define LIBRARY_POSITION 25
/* LIBRARY_TONE valid options */
#define TONE_LIB_OVERDRIVE 1793
#define TONE_LIB_ROCK1 1794
#define TONE_LIB_ROCK2 1795
#define TONE_LIB_BLUES1 1796
#define TONE_LIB_BLUES2 1797
#define TONE_LIB_METAL1 1798
#define TONE_LIB_METAL2 1799
#define TONE_LIB_COUNTRY1 1800
#define TONE_LIB_COUNTRY2 1801
#define TONE_LIB_WARM_DRIVE 1802
#define TONE_LIB_CRUNCH 1803
#define TONE_LIB_TEXAS_TONE 1804
#define TONE_LIB_ROCKABILLY 1805
#define TONE_LIB_SOLO1 1806
#define TONE_LIB_SOLO2 1807
#define TONE_LIB_ROCKWAH 1808
#define TONE_LIB_CHUNKY 1809
#define TONE_LIB_SMOOTH 1810
#define TONE_LIB_HEAVY 1811
#define TONE_LIB_CLEAN1 1812
#define TONE_LIB_CLEAN2 1813
#define TONE_LIB_BRITISH1 1814
#define TONE_LIB_BRITISH2 1815
#define TONE_LIB_AMERICAN1 1816
#define TONE_LIB_AMERICAN2 1817
#define TONE_LIB_TUBE_DRIVE 1818
#define TONE_LIB_DISTORTION 1819
#define TONE_LIB_SCOOPED 1820
#define TONE_LIB_PUNCHY 1821
#define TONE_LIB_BRIGHT_CLEAN 1822
/* setting LIBRARY_TONE to this option seems to crash device */
#define TONE_LIB_CUSTOM 1792
/* LIBRARY_EFFECTS valid options */
#define EFFECTS_LIB_CHORUS 1857
#define EFFECTS_LIB_PHASER 1858
#define EFFECTS_LIB_FLANGER 1859
#define EFFECTS_LIB_PITCH 1860
#define EFFECTS_LIB_TREMOLO 1861
#define EFFECTS_LIB_ROTARY 1862
#define EFFECTS_LIB_ENVELOPE 1863
#define EFFECTS_LIB_DIGITAL 1864
#define EFFECTS_LIB_ANALOG 1865
#define EFFECTS_LIB_PONG 1866
#define EFFECTS_LIB_MOD 1867
#define EFFECTS_LIB_TAPE 1868
#define EFFECTS_LIB_HALL 1869
#define EFFECTS_LIB_PLATE 1870
#define EFFECTS_LIB_SPRING 1871
#define EFFECTS_LIB_CHORUS_DIGITAL 1872
#define EFFECTS_LIB_CHORUS_DELAY_REVERB 1873
#define EFFECTS_LIB_FLANGER_ANALOG 1874
#define EFFECTS_LIB_PHASER_TAPE 1875
#define EFFECTS_LIB_PHASER_MOD 1876
#define EFFECTS_LIB_PHASER_ROOM 1877
#define EFFECTS_LIB_DIGITAL_HALL 1878
#define EFFECTS_LIB_ANALOG_SPRING 1879
#define EFFECTS_LIB_CHORUS_HALL 1880
#define EFFECTS_LIB_PONG_HALL 1881
#define EFFECTS_LIB_TAPE_SPRING 1882
#define EFFECTS_LIB_TREMOLO_TAPE 1883
#define EFFECTS_LIB_PITCH_DIGITAL 1884
#define EFFECTS_LIB_MOD_PLATE 1885
#define EFFECTS_LIB_ROTARY_TAPE 1886
/* setting EFFECTS_LIBRARY to this option seems to crash device */
#define EFFECTS_LIB_CUSTOM 1856
#define USB_POSITION 0
#define USB_AUDIO_PLAYBACK_MIX 12297
#define USB_AUDIO_LEVEL 12307
enum {
#endif /* DOXYGEN_SHOULD_SKIP_THIS */
typedef enum {
PRESETS_SYSTEM = 0,
PRESETS_USER = 1,
PRESETS_ARTIST = 2,
@@ -458,9 +555,9 @@ enum {
/* Version 2 and later */
PRESETS_EXTERNAL = 6
};
} PresetBank;
enum {
typedef enum {
REQUEST_WHO_AM_I = 0x01,
RECEIVE_WHO_AM_I = 0x02,
@@ -491,7 +588,7 @@ enum {
MOVE_PRESET = 0x39,
REQUEST_MODIFIER_LINKABLE_LIST = 0x3A,
RECEIVE_MOFIFIER_LINKABLE_LIST = 0x3B,
RECEIVE_MODIFIER_LINKABLE_LIST = 0x3B,
REQUEST_PARAMETER_VALUE = 0x40,
RECEIVE_PARAMETER_VALUE = 0x41,
@@ -512,10 +609,11 @@ enum {
ACK = 0x7E,
NACK = 0x7F
};
} MessageID;
void send_message(gint procedure, gchar *data, gint len);
void append_value(GString *msg, guint value);
GString *get_message_by_id(MessageID id);
void set_option(guint id, guint position, guint value);
void switch_preset(guint bank, guint x);
void store_preset_name(int x, const gchar *name);

914
gtkknob.c Normal file
View File

@@ -0,0 +1,914 @@
/*****************************************************************************
*
* Most of this code comes from gAlan 0.2.0, copyright (C) 1999
* 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
*
* 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; either version 2
* of the License, or (at your option) any later version.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*****************************************************************************/
#include <math.h>
#include <stdio.h>
#include <gtk/gtkmain.h>
#include <gtk/gtksignal.h>
#include <gdk/gdkkeysyms.h>
#include "gtkknob.h"
#ifndef M_PI
# define M_PI 3.14159265358979323846 /* pi */
#endif
#ifndef M_1_PI
# define M_1_PI 0.31830988618379067154 /* 1/pi */
#endif
#define SCROLL_DELAY_LENGTH 250
#define KNOB_SIZE 32 /* this should really be read from the knob image */
#define STATE_IDLE 0
#define STATE_PRESSED 1
#define STATE_DRAGGING 2
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_realize(GtkWidget *widget);
static void gtk_knob_size_request(GtkWidget *widget, GtkRequisition *requisition);
static void gtk_knob_size_allocate(GtkWidget *widget, GtkAllocation *allocation);
static gint gtk_knob_expose(GtkWidget *widget, GdkEventExpose *event);
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);
static gint gtk_knob_key_press(GtkWidget *widget, GdkEventKey *event);
static gint gtk_knob_motion_notify(GtkWidget *widget, GdkEventMotion *event);
static gint gtk_knob_timer(GtkKnob *knob);
static void gtk_knob_update_mouse_update(GtkKnob *knob);
static void gtk_knob_update_mouse(GtkKnob *knob, gint x, gint y, gboolean step);
static void gtk_knob_update(GtkKnob *knob);
static void gtk_knob_adjustment_changed(GtkAdjustment *adjustment, gpointer data);
static void gtk_knob_adjustment_value_changed(GtkAdjustment *adjustment, gpointer data);
/* Local data */
static GtkWidgetClass *parent_class = NULL;
/*****************************************************************************
*
* gtk_knob_get_type()
*
*****************************************************************************/
GType
gtk_knob_get_type(void) {
static GType knob_type = 0;
if (!knob_type) {
static const GTypeInfo knob_info = {
sizeof (GtkKnobClass),
NULL,
NULL,
(GClassInitFunc) gtk_knob_class_init,
NULL,
NULL,
sizeof (GtkKnob),
0,
(GInstanceInitFunc) gtk_knob_init,
};
knob_type = g_type_register_static (GTK_TYPE_WIDGET, "GtkKnob", &knob_info, 0);
}
return knob_type;
}
/*****************************************************************************
*
* gtk_knob_class_init()
*
*****************************************************************************/
static void
gtk_knob_class_init (GtkKnobClass *class) {
GtkObjectClass *object_class;
GtkWidgetClass *widget_class;
object_class = (GtkObjectClass*) class;
widget_class = (GtkWidgetClass*) class;
parent_class = g_type_class_ref (GTK_TYPE_WIDGET);
object_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->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;
}
/*****************************************************************************
*
* gtk_knob_init()
*
*****************************************************************************/
static void
gtk_knob_init (GtkKnob *knob) {
knob->policy = GTK_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_knob_new()
*
*****************************************************************************/
GtkWidget *
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);
gtk_knob_set_animation (knob, anim);
if (!adjustment) {
adjustment = (GtkAdjustment*) gtk_adjustment_new (0.0, 0.0, 0.0,
0.0, 0.0, 0.0);
}
gtk_knob_set_adjustment (knob, adjustment);
return GTK_WIDGET (knob);
}
/*****************************************************************************
*
* gtk_knob_destroy()
*
*****************************************************************************/
static void
gtk_knob_destroy(GtkObject *object) {
GtkKnob *knob;
g_return_if_fail (object != NULL);
g_return_if_fail (GTK_IS_KNOB (object));
knob = GTK_KNOB (object);
if (knob->adjustment) {
g_object_unref (knob->adjustment);
knob->adjustment = NULL;
}
/* FIXME: needs ref counting for automatic GtkKnobAnim cleanup
if (knob->anim) {
gtk_knob_anim_unref (knob->anim);
knob->anim = NULL;
}
*/
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);
}
}
/*****************************************************************************
*
* gtk_knob_get_adjustment()
*
*****************************************************************************/
GtkAdjustment*
gtk_knob_get_adjustment(GtkKnob *knob) {
g_return_val_if_fail (knob != NULL, NULL);
g_return_val_if_fail (GTK_IS_KNOB (knob), NULL);
return knob->adjustment;
}
/*****************************************************************************
*
* gtk_knob_set_update_policy()
*
*****************************************************************************/
void
gtk_knob_set_update_policy(GtkKnob *knob, GtkUpdateType policy) {
g_return_if_fail (knob != NULL);
g_return_if_fail (GTK_IS_KNOB (knob));
knob->policy = policy;
}
/*****************************************************************************
*
* gtk_knob_set_adjustment()
*
*****************************************************************************/
void
gtk_knob_set_adjustment(GtkKnob *knob, GtkAdjustment *adjustment) {
g_return_if_fail (knob != NULL);
g_return_if_fail (GTK_IS_KNOB (knob));
if (knob->adjustment) {
g_signal_handlers_disconnect_matched(knob->adjustment,
G_SIGNAL_MATCH_DATA,
0, 0, NULL, NULL,
knob);
g_object_unref (knob->adjustment);
}
knob->adjustment = adjustment;
g_object_ref (GTK_OBJECT (knob->adjustment));
g_object_ref_sink (GTK_OBJECT (knob->adjustment));
g_signal_connect (adjustment, "changed",
(GCallback) gtk_knob_adjustment_changed,
knob);
g_signal_connect (adjustment, "value_changed",
(GCallback) gtk_knob_adjustment_value_changed,
knob);
knob->old_value = adjustment->value;
knob->old_lower = adjustment->lower;
knob->old_upper = adjustment->upper;
gtk_knob_update (knob);
}
/*****************************************************************************
*
* gtk_knob_realize()
*
*****************************************************************************/
static void
gtk_knob_realize(GtkWidget *widget) {
GtkKnob *knob;
GdkWindowAttr attributes;
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);
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.event_mask =
gtk_widget_get_events (widget) |
GDK_EXPOSURE_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;
widget->window = gdk_window_new (widget->parent->window,
&attributes, attributes_mask);
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);
}
/*****************************************************************************
*
* gtk_knob_size_request()
*
*****************************************************************************/
static void
gtk_knob_size_request (GtkWidget *widget, GtkRequisition *requisition) {
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;
}
/*****************************************************************************
*
* gtk_knob_size_allocate()
*
*****************************************************************************/
static void
gtk_knob_size_allocate (GtkWidget *widget, GtkAllocation *allocation) {
GtkKnob *knob;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_KNOB (widget));
g_return_if_fail (allocation != NULL);
widget->allocation = *allocation;
knob = GTK_KNOB (widget);
if (GTK_WIDGET_REALIZED (widget)) {
gdk_window_move_resize (widget->window,
allocation->x, allocation->y,
allocation->width, allocation->height);
}
}
/*****************************************************************************
*
* gtk_knob_expose()
*
*****************************************************************************/
static gint
gtk_knob_expose(GtkWidget *widget, GdkEventExpose *event) {
GtkKnob *knob;
gfloat 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);
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 = (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);
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);
}
return FALSE;
}
/*****************************************************************************
*
* gtk_knob_scroll()
*
*****************************************************************************/
static gint
gtk_knob_scroll(GtkWidget *widget, GdkEventScroll *event) {
GtkKnob *knob;
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);
knob = GTK_KNOB (widget);
switch (event->direction) {
case GDK_SCROLL_UP:
knob->adjustment->value += knob->adjustment->step_increment;
g_signal_emit_by_name (knob->adjustment, "value_changed");
break;
case GDK_SCROLL_DOWN:
knob->adjustment->value -= knob->adjustment->step_increment;
g_signal_emit_by_name (knob->adjustment, "value_changed");
break;
default:
break;
}
return FALSE;
}
/*****************************************************************************
*
* gtk_knob_button_press()
*
*****************************************************************************/
static gint
gtk_knob_button_press(GtkWidget *widget, GdkEventButton *event) {
GtkKnob *knob;
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);
knob = GTK_KNOB (widget);
switch (knob->state) {
case STATE_IDLE:
switch (event->button) {
case 1:
case 3:
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);
g_signal_emit_by_name (knob->adjustment, "value_changed");
break;
}
break;
}
return FALSE;
}
/*****************************************************************************
*
* gtk_knob_button_release()
*
*****************************************************************************/
static gint
gtk_knob_button_release(GtkWidget *widget, GdkEventButton *event) {
GtkKnob *knob;
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);
knob = GTK_KNOB (widget);
switch (knob->state) {
case STATE_PRESSED:
knob->state = STATE_IDLE;
break;
case STATE_DRAGGING:
knob->state = STATE_IDLE;
switch (event->button) {
case 1:
case 3:
if (knob->policy != GTK_UPDATE_CONTINUOUS
&& knob->old_value != knob->adjustment->value)
{
g_signal_emit_by_name (knob->adjustment, "value_changed");
}
break;
}
break;
}
return FALSE;
}
static gint gtk_knob_key_press(GtkWidget *widget, GdkEventKey *event)
{
GtkKnob *knob;
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);
knob = GTK_KNOB (widget);
switch (event->keyval) {
case GDK_Up:
if (GTK_WIDGET_HAS_FOCUS (widget))
{
gtk_adjustment_set_value (knob->adjustment,
knob->old_value + knob->adjustment->step_increment);
return TRUE;
}
return FALSE;
case GDK_Down:
if (GTK_WIDGET_HAS_FOCUS (widget))
{
gtk_adjustment_set_value (knob->adjustment,
knob->old_value - knob->adjustment->step_increment);
return TRUE;
}
return FALSE;
default:
break;
}
return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
}
/*****************************************************************************
*
* gtk_knob_motion_notify()
*
*****************************************************************************/
static gint
gtk_knob_motion_notify(GtkWidget *widget, GdkEventMotion *event) {
GtkKnob *knob;
GdkModifierType mods;
gint x, y;
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);
knob = GTK_KNOB (widget);
x = event->x;
y = event->y;
if (event->is_hint || (event->window != widget->window)) {
gdk_window_get_pointer (widget->window, &x, &y, &mods);
}
switch (knob->state) {
case STATE_PRESSED:
knob->state = STATE_DRAGGING;
/* fall through */
case STATE_DRAGGING:
if (mods & GDK_BUTTON1_MASK) {
gtk_knob_update_mouse (knob, x, y, TRUE);
return TRUE;
}
else if (mods & GDK_BUTTON3_MASK) {
gtk_knob_update_mouse (knob, x, y, FALSE);
return TRUE;
}
break;
}
return FALSE;
}
/*****************************************************************************
*
* gtk_knob_timer()
*
*****************************************************************************/
static gint
gtk_knob_timer(GtkKnob *knob) {
g_return_val_if_fail (knob != NULL, FALSE);
g_return_val_if_fail (GTK_IS_KNOB (knob), FALSE);
if (knob->policy == GTK_UPDATE_DELAYED) {
g_signal_emit_by_name (knob->adjustment, "value_changed");
}
/* don't keep running this timer */
return FALSE;
}
/*****************************************************************************
*
* gtk_knob_update_mouse_update()
*
*****************************************************************************/
static void
gtk_knob_update_mouse_update(GtkKnob *knob) {
if (knob->policy == GTK_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->timer) {
g_source_remove (knob->timer);
}
knob->timer = g_timeout_add (SCROLL_DELAY_LENGTH,
(GSourceFunc) gtk_knob_timer,
(gpointer) knob);
}
}
}
/*****************************************************************************
*
* gtk_knob_update_mouse()
*
*****************************************************************************/
static void
gtk_knob_update_mouse(GtkKnob *knob, gint x, gint y, gboolean step) {
gfloat old_value, new_value, dv, dh;
gfloat angle;
g_return_if_fail (knob != NULL);
g_return_if_fail (GTK_IS_KNOB (knob));
old_value = knob->adjustment->value;
angle = atan2f (-y + (knob->height >> 1), x - (knob->width >> 1));
/* inverted cartesian graphics coordinate system */
dv = knob->saved_y - y;
dh = x - knob->saved_x;
knob->saved_x = x;
knob->saved_y = y;
if ((x >= 0) && (x <= knob->width)) {
dh = 0; /* dead zone */
} else {
angle = cosf (angle);
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 = MAX (MIN (new_value, knob->adjustment->upper),
knob->adjustment->lower);
knob->adjustment->value = new_value;
if (knob->adjustment->value != old_value) {
gtk_knob_update_mouse_update (knob);
}
}
/*****************************************************************************
*
* gtk_knob_update()
*
*****************************************************************************/
static void
gtk_knob_update(GtkKnob *knob) {
gfloat new_value;
g_return_if_fail (knob != NULL);
g_return_if_fail (GTK_IS_KNOB (knob));
if (knob->adjustment->step_increment == 1) {
new_value = floor (knob->adjustment->value + 0.5);
}
else {
new_value = knob->adjustment->value;
}
if (new_value < knob->adjustment->lower) {
new_value = knob->adjustment->lower;
}
if (new_value > knob->adjustment->upper) {
new_value = knob->adjustment->upper;
}
if (new_value != knob->adjustment->value) {
knob->adjustment->value = new_value;
g_signal_emit_by_name (knob->adjustment, "value_changed");
}
gtk_widget_queue_draw (GTK_WIDGET (knob));
}
/*****************************************************************************
*
* gtk_knob_adjustment_changed()
*
*****************************************************************************/
static void
gtk_knob_adjustment_changed(GtkAdjustment *adjustment, gpointer data) {
GtkKnob *knob;
g_return_if_fail (adjustment != NULL);
g_return_if_fail (data != NULL);
knob = GTK_KNOB (data);
if ((knob->old_value != adjustment->value) ||
(knob->old_lower != adjustment->lower) ||
(knob->old_upper != adjustment->upper))
{
gtk_knob_update (knob);
knob->old_value = adjustment->value;
knob->old_lower = adjustment->lower;
knob->old_upper = adjustment->upper;
}
}
/*****************************************************************************
*
* gtk_knob_adjustment_value_changed()
*
*****************************************************************************/
static void
gtk_knob_adjustment_value_changed (GtkAdjustment *adjustment, gpointer data) {
GtkKnob *knob;
g_return_if_fail (adjustment != NULL);
g_return_if_fail (data != NULL);
knob = GTK_KNOB (data);
if (knob->old_value != adjustment->value) {
gtk_knob_update (knob);
knob->old_value = adjustment->value;
}
}
/*****************************************************************************
*
* gtk_knob_set_animation()
*
*****************************************************************************/
void
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)) {
gtk_widget_queue_resize (GTK_WIDGET (knob));
}
}
/*****************************************************************************
*
* gtk_knob_animation_new_from_file()
*
*****************************************************************************/
GtkKnobAnim *
gtk_knob_animation_new_from_file(gchar *filename) {
GtkKnobAnim *anim;
anim = gtk_knob_animation_new_from_file_full (filename, -1, -1, KNOB_SIZE);
return anim;
}
/*****************************************************************************
*
* gtk_knob_animation_new_from_inline()
*
*****************************************************************************/
GtkKnobAnim *
gtk_knob_animation_new_from_inline(const guint8 *pixbuf) {
GtkKnobAnim *anim = g_new0 (GtkKnobAnim, 1);
g_return_val_if_fail((pixbuf != NULL), NULL);
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->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()
*
*****************************************************************************/
void
gtk_knob_animation_free(GtkKnobAnim *anim) {
g_return_if_fail (anim != NULL);
if (anim->pixbuf)
g_object_unref (anim->pixbuf);
g_free (anim);
}

104
gtkknob.h Normal file
View File

@@ -0,0 +1,104 @@
/*****************************************************************************
*
* 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.
*
* 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; either version 2
* of the License, or (at your option) any later version.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*****************************************************************************/
#ifndef __GTK_KNOB_H__
#define __GTK_KNOB_H__
#include <gdk/gdk.h>
#include <gtk/gtkadjustment.h>
#include <gtk/gtkwidget.h>
#ifdef __cplusplus
extern "C" {
#endif
#define GTK_KNOB(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, gtk_knob_get_type(), GtkKnob)
#define GTK_KNOB_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, gtk_knob_get_type(), GtkKnobClass)
#define GTK_IS_KNOB(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, gtk_knob_get_type())
typedef struct _GtkKnob GtkKnob;
typedef struct _GtkKnobClass GtkKnobClass;
typedef struct _GtkKnobAnim GtkKnobAnim;
/* better to make this an object and let widgets ref/deref it perhaps */
struct _GtkKnobAnim {
GdkPixbuf *pixbuf;
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 */
};
struct _GtkKnob {
GtkWidget widget;
/* update policy (GTK_UPDATE_[CONTINUOUS/DELAYED/DISCONTINUOUS]) */
guint policy : 2;
/* State of widget (to do with user interaction) */
guint8 state;
gint saved_x, saved_y;
/* ID of update timer, or 0 if none */
guint32 timer;
/* knob animation */
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;
/* The adjustment object that stores the data for this knob */
GtkAdjustment *adjustment;
};
struct _GtkKnobClass {
GtkWidgetClass parent_class;
};
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_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_file(gchar *filename);
void gtk_knob_set_animation (GtkKnob *knob, GtkKnobAnim *anim);
void gtk_knob_animation_free(GtkKnobAnim *anim);
#ifdef __cplusplus
}
#endif
#endif

442
gui.c
View File

@@ -19,17 +19,44 @@
#include "gui.h"
#include "effects.h"
#include "preset.h"
#include "gtkknob.h"
#include "knob.h"
#ifndef DOXYGEN_SHOULD_SKIP_THIS
extern EffectList effects[];
extern int n_effects;
static gboolean allow_send = FALSE;
#endif /* DOXYGEN_SHOULD_SKIP_THIS */
typedef struct {
GtkObject *widget;
gint id;
gint position;
/* used for combo boxes, if widget isn't combo box, then both value and x are -1 */
gint value; /**< effect type value */
gint x; /**< combo box item number */
} WidgetListElem;
#ifndef DOXYGEN_SHOULD_SKIP_THIS
static GtkKnobAnim *knob_anim = NULL; /* animation used by knobs */
#endif /* DOXYGEN_SHOULD_SKIP_THIS */
static GList *widget_list = NULL; /**< this list contains WidgetListElem data elements */
static gboolean allow_send = FALSE; /**< if FALSE GUI parameter changes won't be sent to device */
/**
* \param parent transient parent, or NULL for none
* \param message error description
*
* Shows error message dialog.
**/
void show_error_message(GtkWidget *parent, gchar *message)
{
g_return_if_fail(message != NULL);
GtkWidget *msg = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
GtkWidget *msg = gtk_message_dialog_new(GTK_WINDOW(parent),
GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
message);
@@ -38,58 +65,90 @@ void show_error_message(GtkWidget *parent, gchar *message)
gtk_widget_destroy(msg);
}
typedef struct {
GtkWidget *widget;
gint id;
gint position;
/* used for combo boxes, if widget isn't combo box, then both value and x are -1 */
gint value; /* effect type value */
gint x; /* combo box item number */
} WidgetListElem;
void value_changed_option_cb(GtkSpinButton *spinbutton, EffectSettings *setting)
/**
* \param adj the object which emitted the signal
* \param setting setting controlled by adj
*
* Sets effect value.
**/
void value_changed_option_cb(GtkAdjustment *adj, EffectSettings *setting)
{
g_return_if_fail(setting != NULL);
if (allow_send) {
gint val = gtk_spin_button_get_value_as_int(spinbutton);
set_option(setting->option, setting->position, val);
gdouble val;
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]);
}
}
}
/**
* \param button the object which emitted the signal
* \param effect effect controlled by button
*
* Turns effect on/off basing on state of button.
**/
void toggled_cb(GtkToggleButton *button, Effect *effect)
{
g_return_if_fail(effect != NULL);
if (allow_send) {
guint val = gtk_toggle_button_get_active(button);
set_option(effect->option, effect->position, val);
set_option(effect->id, effect->position, val);
}
}
static void widget_list_add(GList **list, GtkWidget *widget, gint id, gint position, gint value, gint x)
/**
* \param widget GtkObject to add to widget list
* \param id object controlled ID
* \param position object controlled position
* \param value effect value type (if widget is GtkComboBox, otherwise -1)
* \param x combo box item number (if widget is GtkComboBox, otherwise -1)
*
* Adds widget to widget list.
**/
static void widget_list_add(GtkObject *widget, gint id, gint position, gint value, gint x)
{
WidgetListElem *el;
el = g_malloc(sizeof(WidgetListElem));
el = g_slice_new(WidgetListElem);
el->widget = widget;
el->id = id;
el->position = position;
el->value = value;
el->x = x;
*list = g_list_prepend(*list, el);
widget_list = g_list_prepend(widget_list, el);
}
/**
* \param el widget list element
* \param param parameter to set
*
* Sets widget list element value to param value.
**/
static void apply_widget_setting(WidgetListElem *el, SettingParam *param)
{
if ((el->id == param->id) && (el->position == param->position)) {
if (el->value == -1) {
if (GTK_IS_TOGGLE_BUTTON(el->widget))
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(el->widget), (param->value == 0) ? FALSE : TRUE);
else if (GTK_IS_SPIN_BUTTON(el->widget))
gtk_spin_button_set_value(GTK_SPIN_BUTTON(el->widget), param->value);
else if (GTK_IS_ADJUSTMENT(el->widget))
gtk_adjustment_set_value(GTK_ADJUSTMENT(el->widget), (gdouble)param->value);
} else { /* combo box */
if (el->value == param->value)
gtk_combo_box_set_active(GTK_COMBO_BOX(el->widget), el->x);
@@ -97,8 +156,16 @@ static void apply_widget_setting(WidgetListElem *el, SettingParam *param)
}
}
static void apply_preset_to_gui(GList *list, Preset *preset)
/**
* \param preset preset to sync
*
* Synces GUI with preset.
**/
static void apply_preset_to_gui(Preset *preset)
{
g_return_if_fail(preset != NULL);
g_return_if_fail(widget_list != NULL);
allow_send = FALSE;
GList *iter = preset->params;
@@ -107,68 +174,115 @@ static void apply_preset_to_gui(GList *list, Preset *preset)
iter = iter->next;
if (param != NULL)
g_list_foreach(list, (GFunc)apply_widget_setting, param);
g_list_foreach(widget_list, (GFunc)apply_widget_setting, param);
}
allow_send = TRUE;
}
static void apply_current_preset(GList *list)
/**
* Synces GUI with device current edit buffer.
**/
static void apply_current_preset()
{
GString *msg = get_current_preset();
Preset *preset = create_preset_from_data(msg);
g_string_free(msg, TRUE);
apply_preset_to_gui(list, preset);
apply_preset_to_gui(preset);
preset_free(preset);
}
GtkWidget *create_table(GList **list, EffectSettings *settings, gint amt)
/**
* \param settings effect parameters
* \param amt amount of effect parameters
*
* Creates knobs that allow user to set effect parameters.
*
* \return GtkTable containing necessary widgets to set effect parameters.
**/
GtkWidget *create_table(EffectSettings *settings, gint amt)
{
GtkWidget *table, *label, *widget;
GtkWidget *table, *label, *widget, *knob;
GtkObject *adj;
int x;
table = gtk_table_new(2, amt, FALSE);
table = gtk_table_new(3, amt, FALSE);
for (x = 0; x<amt; x++) {
label = gtk_label_new(settings[x].label);
adj = gtk_adjustment_new(0.0, settings[x].min, settings[x].max, 1.0, 1.0, 0.0);
widget = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 1.0, 0);
g_signal_connect(G_OBJECT(widget), "value-changed", G_CALLBACK(value_changed_option_cb), &settings[x]);
widget_list_add(list, widget, settings[x].option, settings[x].position, -1, -1);
adj = gtk_adjustment_new(0.0, settings[x].values->min, settings[x].values->max,
1.0, /* step increment */
MAX((settings[x].values->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_list_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), widget, 1, 2, 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);
g_signal_connect(G_OBJECT(adj), "value-changed", G_CALLBACK(value_changed_option_cb), &settings[x]);
}
return table;
}
GtkWidget *create_on_off_button(GList **list, Effect *effect)
/**
* \param effect Effect that can be turned on/off
*
* Creates toggle button that allow user to turn effect on/off.
*
* \return GtkToggleButton
**/
GtkWidget *create_on_off_button(Effect *effect)
{
GtkWidget *button = gtk_toggle_button_new_with_label(effect->label);
GtkWidget *button = gtk_check_button_new();
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(toggled_cb), effect);
widget_list_add(list, button, effect->option, effect->position, -1, -1);
widget_list_add(GTK_OBJECT(button), effect->id, effect->position, -1, -1);
return button;
}
typedef struct {
gint id; /* effect group ID (value) */
gint option; /* option ID */
gint position; /* position */
GtkWidget *child; /* child widget */
gint type; /**< effect group type (value) */
gint id; /**< option ID */
gint position; /**< position */
GtkWidget *child; /**< child widget */
} EffectSettingsGroup;
/**
* \param group group to be freed
*
* Frees all memory used by group
**/
void effect_settings_group_free(EffectSettingsGroup *group)
{
/* destroy widget without parent */
if (gtk_widget_get_parent(group->child) == NULL)
gtk_widget_destroy(group->child);
if (group->child != NULL) {
/* destroy widget without parent */
if (gtk_widget_get_parent(group->child) == NULL)
gtk_widget_destroy(group->child);
g_object_unref(group->child);
g_free(group);
g_object_unref(group->child);
}
g_slice_free(EffectSettingsGroup, group);
}
/**
* \param widget the object which emitted the signal
* \param data user data (unused, can be anything)
*
* Switches effect type and shows widgets allowing to set selected effect type parameters.
**/
void combo_box_changed_cb(GtkComboBox *widget, gpointer data)
{
GtkWidget *child;
@@ -186,19 +300,30 @@ void combo_box_changed_cb(GtkComboBox *widget, gpointer data)
g_free(name);
if (settings != NULL && allow_send)
set_option(settings->option, settings->position, settings->id);
set_option(settings->id, settings->position, settings->type);
child = g_object_get_data(G_OBJECT(widget), "active_child");
if (child != NULL) {
gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(gtk_widget_get_parent(vbox))), child);
}
gtk_container_add(GTK_CONTAINER(gtk_widget_get_parent(gtk_widget_get_parent(vbox))), settings->child);
gtk_widget_show_all(gtk_widget_get_parent(gtk_widget_get_parent(vbox)));
if (settings->child != NULL) {
gtk_container_add(GTK_CONTAINER(gtk_widget_get_parent(gtk_widget_get_parent(vbox))), settings->child);
gtk_widget_show_all(gtk_widget_get_parent(gtk_widget_get_parent(vbox)));
}
g_object_set_data(G_OBJECT(widget), "active_child", settings->child);
}
}
GtkWidget *create_widget_container(GList **list, EffectGroup *group, gint amt)
/**
* \param group Effect type groups
* \param amt amount of effect groups
*
* Creates widget allowing user to choose effect type.
*
* \return widget that allow user to set effect type.
**/
GtkWidget *create_widget_container(EffectGroup *group, gint amt)
{
GtkWidget *vbox;
GtkWidget *widget;
@@ -221,51 +346,73 @@ GtkWidget *create_widget_container(GList **list, EffectGroup *group, gint amt)
gtk_combo_box_append_text(GTK_COMBO_BOX(combo_box), group[x].label);
cmbox_no++;
widget = create_table(list, group[x].settings, group[x].settings_amt);
g_object_ref_sink(widget);
if ((group[x].settings != NULL) && (group[x].settings > 0)) {
widget = create_table(group[x].settings, group[x].settings_amt);
g_object_ref_sink(widget);
} else
widget = NULL;
settings = g_malloc(sizeof(EffectSettingsGroup));
settings = g_slice_new(EffectSettingsGroup);
settings->id = group[x].id;
settings->option = group[x].option;
settings->type = group[x].type;
settings->position = group[x].position;
settings->child = widget;
widget_list_add(list, combo_box, group[x].option, group[x].position, group[x].id, x);
widget_list_add(GTK_OBJECT(combo_box), group[x].id, group[x].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 {
widget = create_table(list, group[x].settings, group[x].settings_amt);
widget = create_table(group[x].settings, group[x].settings_amt);
gtk_container_add(GTK_CONTAINER(vbox), widget);
}
}
return vbox;
};
}
GtkWidget *create_vbox(GList **list, Effect *widgets, gint amt)
/**
* \param widgets Effect descriptions
* \param amt amount of effect descriptions
* \param label frame label (can be NULL)
*
* Creates frame (with optional label) containing widgets allowing user to set effect options.
*
* \return widget that allow user to set effect options.
**/
GtkWidget *create_vbox(Effect *widgets, gint amt, gchar *label)
{
GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *widget;
GtkWidget *table;
GtkWidget *container;
GtkWidget *frame;
int x;
vbox = gtk_vbox_new(FALSE, 0);
hbox = gtk_hbox_new(FALSE, 0);
frame = gtk_frame_new(label);
gtk_box_set_homogeneous(GTK_BOX(hbox), TRUE);
vbox = gtk_vbox_new(FALSE, 0);
table = gtk_table_new(2, amt, FALSE);
gtk_table_set_col_spacings(GTK_TABLE(table), 2);
for (x = 0; x<amt; x++) {
widget = create_on_off_button(list, &widgets[x]);
gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 2);
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);
} 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);
}
table = create_widget_container(list, widgets[x].group, widgets[x].group_amt);
gtk_box_pack_start(GTK_BOX(hbox), table, TRUE, TRUE, 2);
container = create_widget_container(widgets[x].group, widgets[x].group_amt);
gtk_table_attach_defaults(GTK_TABLE(table), container, 1, 2, x, x+1);
}
gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 2);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 2);
return vbox;
gtk_container_add(GTK_CONTAINER(frame), vbox);
return frame;
}
enum {
@@ -275,6 +422,14 @@ enum {
NUM_COLUMNS
};
/**
* \param treeview the object which emitted the signal
* \param path the GtkTreePath for the activated row
* \param column the GtkTreeViewColumn in which the activation occurred
* \param model model holding preset names
*
* Sets active device preset to preset selected by user.
**/
void row_activate_cb(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *column, GtkTreeModel *model) {
GtkTreeIter iter;
gint id;
@@ -284,13 +439,18 @@ void row_activate_cb(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn
gtk_tree_model_get(model, &iter, PRESET_NUMBER_COLUMN, &id, PRESET_BANK_COLUMN, &bank, -1);
if ((bank != -1) && (id != -1)) {
GList **list = g_object_get_data(G_OBJECT(treeview), "widgets-list");
switch_preset(bank, id);
if (*list != NULL)
apply_current_preset(*list);
apply_current_preset();
}
}
/**
* \param model model to fill
* \param bank preset bank
* \param name preset bank description visible to user
*
* Appends to model preset names found in device preset bank.
**/
static void fill_store_with_presets(GtkTreeStore *model, guint bank, gchar *name)
{
GtkTreeIter iter;
@@ -298,6 +458,8 @@ static void fill_store_with_presets(GtkTreeStore *model, guint bank, gchar *name
int x;
GStrv presets = query_preset_names(bank);
g_return_if_fail(presets != NULL);
gtk_tree_store_append(model, &iter, NULL);
gtk_tree_store_set(model, &iter,
PRESET_NAME_COLUMN, name,
@@ -306,23 +468,37 @@ static void fill_store_with_presets(GtkTreeStore *model, guint bank, gchar *name
-1);
for (x=0; x<g_strv_length(presets); x++) {
gchar *tmp = g_strdup_printf("%d - %s", x+1, presets[x]);
gtk_tree_store_append(model, &child_iter, &iter);
gtk_tree_store_set(model, &child_iter,
PRESET_NAME_COLUMN, presets[x],
PRESET_NAME_COLUMN, tmp,
PRESET_NUMBER_COLUMN, x,
PRESET_BANK_COLUMN, bank,
-1);
g_free(tmp);
}
g_strfreev(presets);
}
/**
* \param model model to fill
*
* Fills model with preset names found on device.
**/
static void fill_store(GtkTreeStore *model)
{
fill_store_with_presets(model, PRESETS_USER, "User Presets");
fill_store_with_presets(model, PRESETS_SYSTEM, "System Presets");
}
GtkWidget *create_preset_tree(GList **list)
/**
* Creates treeview showing list of presets available on device.
*
* \return treeview containing all preset names found on device.
**/
GtkWidget *create_preset_tree()
{
GtkWidget *treeview;
GtkTreeStore *store;
@@ -344,11 +520,15 @@ GtkWidget *create_preset_tree(GList **list)
g_signal_connect(G_OBJECT(treeview), "realize", G_CALLBACK(gtk_tree_view_expand_all), NULL);
g_signal_connect(G_OBJECT(treeview), "row-activated", G_CALLBACK(row_activate_cb), GTK_TREE_MODEL(store));
g_object_set_data(G_OBJECT(treeview), "widgets-list", list);
return treeview;
}
/**
* \param window application toplevel window
* \param default_name default preset name
*
* Shows window allowing user to store current edit buffer.
**/
static void show_store_preset_window(GtkWidget *window, gchar *default_name)
{
GtkWidget *dialog, *cmbox, *entry, *table, *label;
@@ -397,12 +577,22 @@ static void show_store_preset_window(GtkWidget *window, gchar *default_name)
gtk_widget_destroy(dialog);
}
/**
* \param action the object which emitted the signal
*
* Shows store preset window.
**/
static void action_store_cb(GtkAction *action)
{
GtkWidget *window = g_object_get_data(G_OBJECT(action), "window");
show_store_preset_window(window, NULL);
}
/**
* \param action the object which emitted the signal
*
* Shows about dialog.
**/
static void action_show_about_dialog_cb(GtkAction *action)
{
static const gchar * const authors[] = {
@@ -421,6 +611,8 @@ static void action_show_about_dialog_cb(GtkAction *action)
NULL);
}
#ifndef DOXYGEN_SHOULD_SKIP_THIS
typedef struct {
gchar *name;
gchar *suffix;
@@ -429,8 +621,17 @@ typedef struct {
static SupportedFileTypes file_types[] = {
{"RP250Preset", "*.rp250p"},
};
static guint n_file_types = G_N_ELEMENTS(file_types);
#endif /* DOXYGEN_SHOULD_SKIP_THIS */
/**
* \param action the object which emitted the signal
*
* Shows file chooser dialog.
* If user opens valid preset file, the preset gets applied to edit buffer and store preset window is shown.
**/
static void action_open_preset_cb(GtkAction *action)
{
static GtkWidget *dialog = NULL;
@@ -439,7 +640,6 @@ static void action_open_preset_cb(GtkAction *action)
return;
GtkWidget *window = g_object_get_data(G_OBJECT(action), "window");
GList **list = g_object_get_data(G_OBJECT(action), "widget-list");
dialog = gtk_file_chooser_dialog_new("Open Preset", GTK_WINDOW(window),
GTK_FILE_CHOOSER_ACTION_OPEN,
@@ -465,11 +665,15 @@ static void action_open_preset_cb(GtkAction *action)
gboolean loaded = FALSE;
while (!loaded && gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
GError *error = NULL;
gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
Preset *preset = create_preset_from_xml_file(filename);
if (preset != NULL) {
if (list != NULL)
apply_preset_to_gui(*list, preset);
Preset *preset = create_preset_from_xml_file(filename, &error);
if (error) {
show_error_message(window, error->message);
g_error_free(error);
error = NULL;
} else if (preset != NULL) {
apply_preset_to_gui(preset);
gtk_widget_hide(dialog);
@@ -518,27 +722,35 @@ static void action_open_preset_cb(GtkAction *action)
dialog = NULL;
}
/**
* \param list widget list to be freed
*
* Frees all memory used by widget list.
*/
static void widget_list_free(GList *list)
{
GList *iter;
for (iter = list; iter; iter = iter->next) {
g_free(iter->data);
g_slice_free(WidgetListElem, iter->data);
}
g_list_free(list);
}
/**
* \param action the object which emitted the signal
*
* Destroys action object "window" data, then stops gtk main loop.
**/
static void action_quit_cb(GtkAction *action)
{
GtkWidget *window = g_object_get_data(G_OBJECT(action), "window");
GList **list = g_object_get_data(G_OBJECT(action), "widget-list");
widget_list_free(*list);
*list = NULL;
gtk_widget_destroy(window);
gtk_main_quit();
}
#ifndef DOXYGEN_SHOULD_SKIP_THIS
static GtkActionEntry entries[] = {
{"File", NULL, "_File"},
{"Preset", NULL, "_Preset"},
@@ -567,16 +779,32 @@ static const gchar *menu_info =
" </menubar>"
"</ui>";
static void add_action_data(GtkUIManager *ui, const gchar *path, GtkWidget *window, GList **list)
#endif /* DOXYGEN_SHOULD_SKIP_THIS */
/**
* \param ui GtkUIManager to lookup actions
* \param path path to action
* \param window toplevel window
*
* Sets action object "window" data to toplevel window.
**/
static void add_action_data(GtkUIManager *ui, const gchar *path, GtkWidget *window)
{
GtkAction *action;
action = gtk_ui_manager_get_action(ui, path);
g_return_if_fail(action != NULL);
g_object_set_data(G_OBJECT(action), "window", window);
g_object_set_data(G_OBJECT(action), "widget-list", list);
}
static void add_menubar(GList **list, GtkWidget *window, GtkWidget *vbox)
/**
* \param window toplevel window
* \param vbox vbox to hold menubar
*
* Creates menubar (adds accel group to toplevel window as well) and packs it into vbox.
**/
static void add_menubar(GtkWidget *window, GtkWidget *vbox)
{
GtkUIManager *ui;
GtkActionGroup *actions;
@@ -599,22 +827,24 @@ static void add_menubar(GList **list, GtkWidget *window, GtkWidget *vbox)
gtk_ui_manager_get_widget(ui, "/MenuBar"),
FALSE, FALSE, 0);
add_action_data(ui, "/MenuBar/File/Quit", window, list);
add_action_data(ui, "/MenuBar/File/Open", window, list);
add_action_data(ui, "/MenuBar/Preset/Store", window, list);
add_action_data(ui, "/MenuBar/Help/About", window, list);
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/Help/About", window);
g_object_unref(ui);
}
void create_window()
/**
* Creates main window.
**/
void gui_create()
{
GtkWidget *window;
GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *widget;
GtkWidget *sw; /* scrolled window to carry preset treeview */
static GList *list = NULL; /* widget list (every data element is WidgetListElem) */
gint x;
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
@@ -623,7 +853,7 @@ void create_window()
vbox = gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
add_menubar(&list, window, vbox);
add_menubar(window, vbox);
hbox = gtk_hbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(vbox), hbox);
@@ -632,23 +862,37 @@ void create_window()
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
gtk_box_pack_start(GTK_BOX(hbox), sw, FALSE, FALSE, 0);
widget = create_preset_tree(&list);
widget = create_preset_tree();
gtk_container_add(GTK_CONTAINER(sw), widget);
vbox = gtk_vbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 2);
knob_anim = gtk_knob_animation_new_from_inline(knob_pixbuf);
for (x = 0; x<n_effects; x++) {
if ((x % 3) == 0) {
hbox = gtk_hbox_new(TRUE, 0);
if ((x % ((n_effects+1)/2)) == 0) {
hbox = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 2);
}
widget = create_vbox(&list, effects[x].effect, effects[x].amt);
widget = create_vbox(effects[x].effect, effects[x].amt, effects[x].label);
gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 2);
}
apply_current_preset(list);
apply_current_preset();
gtk_widget_show_all(window);
g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(gtk_main_quit), NULL);
}
/**
* Frees memory allocated by gui_create which is not explicitly freed when main window is destroyed.
**/
void gui_free()
{
widget_list_free(widget_list);
widget_list = NULL;
gtk_knob_animation_free(knob_anim);
knob_anim = NULL;
}

3
gui.h
View File

@@ -18,6 +18,7 @@
#define GDIGI_GUI_H
void show_error_message(GtkWidget *parent, gchar *message);
void create_window();
void gui_create();
void gui_free();
#endif /* GDIGI_GUI_H */

BIN
knob.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -19,6 +19,8 @@
#include <string.h>
#include "preset.h"
#ifndef DOXYGEN_SHOULD_SKIP_THIS
enum {
PARSER_TYPE_NOT_SET = -1,
PARSER_TYPE_PRESET_NAME = 0,
@@ -51,7 +53,7 @@ static void XMLCALL start(void *data, const char *el, const char **attr) {
if (ad->preset->params != NULL)
g_message("Params aleady exists!");
} else if (g_strcmp0(el, "Param") == 0) {
SettingParam *param = (SettingParam *)g_malloc(sizeof(SettingParam));
SettingParam *param = g_slice_new(SettingParam);
param->id = -1;
param->position = -1;
param->value = -1;
@@ -112,27 +114,34 @@ static void XMLCALL text_cb(void *data, const char* text, int len)
g_free(value);
}
/*
Parses preset file
On success returns Preset which must be freed using preset_free, or NULL on error
*/
Preset *create_preset_from_xml_file(gchar *filename)
#endif /* DOXYGEN_SHOULD_SKIP_THIS */
/**
* \param filename valid path to file
* \param error return location for an error
*
* Tries to open file pointed by path, then parses it.
*
* \return Preset which must be freed using preset_free, or NULL on error.
**/
Preset *create_preset_from_xml_file(gchar *filename, GError **error)
{
GFile *file;
GError *error = NULL;
GError *err = NULL;
gchar *contents;
file = g_file_new_for_path(filename);
if (g_file_get_contents(filename, &contents, NULL, &error) == FALSE) {
g_message("Failed to get %s contents: %s", filename, error->message);
g_error_free(error);
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;
}
AppData *ad = (AppData *) g_malloc(sizeof(AppData));
AppData *ad = g_slice_new(AppData);
ad->depth = 0;
ad->preset = g_malloc(sizeof(Preset));
ad->preset = g_slice_new(Preset);
ad->preset->name = NULL;
ad->preset->params = NULL;
ad->id = PARSER_TYPE_NOT_SET;
@@ -144,11 +153,11 @@ Preset *create_preset_from_xml_file(gchar *filename)
XML_SetCharacterDataHandler(p, text_cb);
if (XML_Parse(p, contents, strlen(contents), XML_TRUE) != XML_STATUS_OK) {
g_warning("Parse error at line %d:\n%s",
(int)XML_GetCurrentLineNumber(p),
XML_ErrorString(XML_GetErrorCode(p)));
g_set_error(error, 0, 0, "Parse error at line %d:\n%s",
(int)XML_GetCurrentLineNumber(p),
XML_ErrorString(XML_GetErrorCode(p)));
preset_free(ad->preset);
g_free(ad);
g_slice_free(AppData, ad);
g_free(contents);
g_object_unref(file);
return NULL;
@@ -158,13 +167,20 @@ Preset *create_preset_from_xml_file(gchar *filename)
preset->params = g_list_reverse(preset->params);
XML_ParserFree(p);
g_free(ad);
g_slice_free(AppData, ad);
g_free(contents);
g_object_unref(file);
return preset;
}
/**
* \param data unpacked RECEIVE_PRESET_PARAMETERS message
*
* Parses message
*
* \return Preset which must be freed using preset_free, or NULL on error.
**/
Preset *create_preset_from_data(GString *data)
{
gint total;
@@ -180,7 +196,7 @@ Preset *create_preset_from_data(GString *data)
total = (unsigned char)data->str[x];
x++;
Preset *preset = g_malloc(sizeof(Preset));
Preset *preset = g_slice_new(Preset);
preset->name = NULL; /* TODO */
preset->params = NULL;
@@ -200,7 +216,7 @@ Preset *create_preset_from_data(GString *data)
x+=tmp;
}
n++;
SettingParam *param = (SettingParam *)g_malloc(sizeof(SettingParam));
SettingParam *param = g_slice_new(SettingParam);
param->id = id;
param->position = position;
param->value = value;
@@ -213,17 +229,25 @@ Preset *create_preset_from_data(GString *data)
return preset;
}
/**
* \param preset preset to be freed
*
* Frees all memory used by preset.
**/
void preset_free(Preset *preset)
{
g_return_if_fail(preset != NULL);
if (preset->params != NULL) {
g_list_foreach(preset->params, (GFunc)g_free, NULL);
GList *iter;
for (iter = preset->params; iter; iter = iter->next) {
g_slice_free(SettingParam, iter->data);
}
g_list_free(preset->params);
}
if (preset->name != NULL)
g_free(preset->name);
g_free(preset);
g_slice_free(Preset, preset);
}

View File

@@ -30,7 +30,7 @@ typedef struct {
GList *params;
} Preset;
Preset *create_preset_from_xml_file(gchar *filename);
Preset *create_preset_from_xml_file(gchar *filename, GError **error);
Preset *create_preset_from_data(GString *data);
void preset_free(Preset *preset);

View File

@@ -210,9 +210,6 @@ void test_presets()
for (x=0; x<=60; x++)
switch_preset(PRESETS_SYSTEM, x);
for (x=0; x<=99; x++)
set_preset_level(x);
}
void test_pickups()
@@ -480,7 +477,7 @@ void test_chorusfx()
set_option(IPS_KEY, CHORUSFX_POSITION, IPS_BB);
set_option(IPS_KEY, CHORUSFX_POSITION, IPS_B);
set_option(IPS_KEY, CHORUSFX_POSITION, IPS_C);
set_option(IPS_KEY, CHORUSFX_POSITION, IPS_DD);
set_option(IPS_KEY, CHORUSFX_POSITION, IPS_DB);
set_option(IPS_KEY, CHORUSFX_POSITION, IPS_D);
set_option(IPS_KEY, CHORUSFX_POSITION, IPS_EB);
set_option(IPS_SCALE, CHORUSFX_POSITION, IPS_MAJOR);