13 Commits
0.1.2 ... 0.1.3

Author SHA1 Message Date
Tomasz Moń
4740bef10f update TODO 2009-03-01 20:23:01 +01:00
Tomasz Moń
968d2947a7 set gui widgets to proper values after loading xml file 2009-03-01 19:00:35 +01:00
Tomasz Moń
e2cb03ab32 add store preset window 2009-03-01 13:31:15 +01:00
Tomasz Moń
ce5fe3fe0c add menubar 2009-03-01 11:57:27 +01:00
Tomasz Moń
966fc748af add store_preset_name 2009-03-01 10:05:24 +01:00
Tomasz Moń
709406f3ef add preset.c and preset.h 2009-03-01 09:52:31 +01:00
Tomasz Moń
ce522227c7 add amp types 2009-02-28 00:17:54 +01:00
Tomasz Moń
7d13b2a7ea add preset treeview 2009-02-27 23:28:34 +01:00
Tomasz Moń
d4c86b3a60 use GOptionContext instead of getopt 2009-02-27 12:57:52 +01:00
Tomasz Moń
ca23f2c94c rename query_user_presets to query_preset_names, make it return GStrv 2009-02-26 22:57:08 +01:00
Tomasz Moń
bfc285ad0f add some defines 2009-02-26 20:32:35 +01:00
Tomasz Moń
4e5f2438d3 add query user presets 2009-02-26 18:21:01 +01:00
Tomasz Moń
10aac46dde Added tag 0.1.2 for changeset 7a31bc520752 2009-02-25 22:25:57 +01:00
13 changed files with 931 additions and 61 deletions

View File

@@ -1,7 +1,7 @@
CC = gcc
CFLAGS = `pkg-config --cflags glib-2.0 gtk+-2.0` -Wall -g
CFLAGS = `pkg-config --cflags glib-2.0 gio-2.0 gtk+-2.0` -Wall -g
OFLAG = -o
LIBS = `pkg-config --libs glib-2.0 gtk+-2.0 alsa`
LIBS = `pkg-config --libs glib-2.0 gio-2.0 gtk+-2.0 alsa` -lexpat
.SUFFIXES : .o .c
.c.o :
@@ -9,8 +9,8 @@ LIBS = `pkg-config --libs glib-2.0 gtk+-2.0 alsa`
all: gdigi
gdigi: gdigi.o tests.o gui.o effects.o
$(CC) $(LIBS) $(OFLAG) gdigi gdigi.o tests.o gui.o effects.o
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.o: gdigi.c
@@ -20,6 +20,8 @@ gui.o: gui.c
effects.o: effects.c
preset.o: preset.c
clean:
rm *.o

2
README
View File

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

24
TODO
View File

@@ -1,10 +1,26 @@
-figure out all magic commands
-make complete gui
-amp/cabinet models
-cabinet models
-tone library
-effects library
-effects level
-handling presets (loading, saving, exporting/importing to/from xml patches)
-handling presets (loading, saving, exporting to xml patches)
-buildsystem
-figure out how to get current device settings, start gui with proper values
-fix expression pedal settings (possible types depend on active preset)
-start gui with proper values
To do so we need to figure out reply formatting of command querying preset.
amidi --port=hw:1,0,0 --send-hex F0 00 00 10 00 5E 02 2a 00 04 00 62 F7 --receive=preset
will create file named preset (give a while for it, and then hit ctrl+c)
this file should have around 440 bytes (depends on actual preset)
0x21 byte holds amount of options
from 0x22 byte starts effects configuration which is:
-2 bytes for ID
-1 byte for position
-1 to 3 bytes for value
Each 8th byte (beginning from 0x27) seems to be status byte which describes
whether or not we shall add 0x80 to ID or value and whether or not value
will be multibyte. So far I couldn't figure the exact meaning of those bytes.
To check you can download some patch from DigiTech Sound Community, apply
it to your device, and then do this amidi command.
Open resulting file in hex editor, and open patch file in text editor.
Every ID, position and value found in patch will appear in the binary file.
-fix expression pedal settings (possible types depend on active effects)

View File

@@ -116,9 +116,16 @@ static EffectSettings dist_mp_settings[] = {
{"MP volume", 0.0, 99.0, DIST_MP_VOLUME, DIST_POSITION},
};
static EffectSettings amp_settings[] = {
{"AMP gain", 0.0, 99.0, AMP_GAIN, AMP_POSITION},
{"AMP level", 0.0, 99.0, AMP_LEVEL, AMP_POSITION},
};
static EffectSettings amp_settings2[] = {
{"AMP level", 0.0, 99.0, AMP_LEVEL, AMP_POSITION},
};
static EffectSettings eq_settings[] = {
{"EQ gain", 0.0, 99.0, AMP_GAIN, AMP_POSITION},
{"EQ level", 0.0, 99.0, AMP_LEVEL, AMP_POSITION},
// TODO: make those display propertly (display range -12 to 12)
{"EQ bass", 0.0, 24.0, EQ_BASS, EQ_POSITION},
{"EQ mid", 0.0, 24.0, EQ_MID, EQ_POSITION},
@@ -432,6 +439,31 @@ static EffectGroup reverb_group[] = {
{REVERB_TYPE_EMT240_PLATE, "EMT240 Plate", REVERB_TYPE, REVERB_POSITION, reverb_emt240_plate_settings, G_N_ELEMENTS(reverb_emt240_plate_settings)},
};
static EffectGroup amp_group[] = {
{AMP_TYPE_TWEED_CHAMP, "Tweed Champ", AMP_TYPE, AMP_POSITION, amp_settings, G_N_ELEMENTS(amp_settings)},
{AMP_TYPE_TWEED_DELUXE, "Tweed Deluxe", AMP_TYPE, AMP_POSITION, amp_settings, G_N_ELEMENTS(amp_settings)},
{AMP_TYPE_TWEED_BASSMAN, "Tweed Bassman", AMP_TYPE, AMP_POSITION, amp_settings, G_N_ELEMENTS(amp_settings)},
{AMP_TYPE_BLACKFACE_TWIN, "Blackface Twin", AMP_TYPE, AMP_POSITION, amp_settings, G_N_ELEMENTS(amp_settings)},
{AMP_TYPE_BLACKFACE_DELUXE, "Blackface Deluxe", AMP_TYPE, AMP_POSITION, amp_settings, G_N_ELEMENTS(amp_settings)},
{AMP_TYPE_SUPER_LEAD_PLEXI, "Super Lead Plexi", AMP_TYPE, AMP_POSITION, amp_settings, G_N_ELEMENTS(amp_settings)},
{AMP_TYPE_MASTER_VOLUME, "Master Volume", AMP_TYPE, AMP_POSITION, amp_settings, G_N_ELEMENTS(amp_settings)},
{AMP_TYPE_JCM800, "JCM800", AMP_TYPE, AMP_POSITION, amp_settings, G_N_ELEMENTS(amp_settings)},
{AMP_TYPE_JCM900, "JCM900", AMP_TYPE, AMP_POSITION, amp_settings, G_N_ELEMENTS(amp_settings)},
{AMP_TYPE_AC15, "AC15", AMP_TYPE, AMP_POSITION, amp_settings, G_N_ELEMENTS(amp_settings)},
{AMP_TYPE_AC30TB, "AC30TB", AMP_TYPE, AMP_POSITION, amp_settings, G_N_ELEMENTS(amp_settings)},
{AMP_TYPE_HIWATT_100, "Hiwatt 100", AMP_TYPE, AMP_POSITION, amp_settings, G_N_ELEMENTS(amp_settings)},
{AMP_TYPE_BOOGIE_MARK_II, "Boogie Mark II", AMP_TYPE, AMP_POSITION, amp_settings, G_N_ELEMENTS(amp_settings)},
{AMP_TYPE_DUAL_RECTIFIER, "Dual Rectifier", AMP_TYPE, AMP_POSITION, amp_settings, G_N_ELEMENTS(amp_settings)},
{AMP_TYPE_MATCHLESS_HC30, "Matchless HC30", AMP_TYPE, AMP_POSITION, amp_settings, G_N_ELEMENTS(amp_settings)},
{AMP_TYPE_SOLO, "Solo", AMP_TYPE, AMP_POSITION, amp_settings, G_N_ELEMENTS(amp_settings)},
{AMP_TYPE_METAL, "Metal", AMP_TYPE, AMP_POSITION, amp_settings, G_N_ELEMENTS(amp_settings)},
{AMP_TYPE_BRIGHT, "Bright", AMP_TYPE, AMP_POSITION, amp_settings, G_N_ELEMENTS(amp_settings)},
{AMP_TYPE_CLEAN, "Clean", AMP_TYPE, AMP_POSITION, amp_settings, G_N_ELEMENTS(amp_settings)},
{AMP_TYPE_HIGH_GAIN, "High Gain", AMP_TYPE, AMP_POSITION, amp_settings, G_N_ELEMENTS(amp_settings)},
{AMP_TYPE_ACOUSTIC, "Acoustic", AMP_TYPE, AMP_POSITION, amp_settings2, G_N_ELEMENTS(amp_settings2)},
{AMP_TYPE_DIRECT, "Direct", AMP_TYPE, AMP_POSITION, amp_settings2, G_N_ELEMENTS(amp_settings2)},
};
static EffectGroup eq_group[] = {
{EQ_TYPE_BRIGHT, "Bright", EQ_TYPE, EQ_POSITION, eq_settings, G_N_ELEMENTS(eq_settings)},
{EQ_TYPE_MIDBOOST, "Mid Boost", EQ_TYPE, EQ_POSITION, eq_settings, G_N_ELEMENTS(eq_settings)},
@@ -467,12 +499,17 @@ static Effect reverb_effect[] = {
{"Reverb", REVERB_ON_OFF, REVERB_POSITION, reverb_group, G_N_ELEMENTS(reverb_group)},
};
static Effect amp_effect[] = {
{"Amp", AMP_ON_OFF, AMP_POSITION, amp_group, G_N_ELEMENTS(amp_group)},
};
static Effect eq_effect[] = {
{"EQ", EQ_ON_OFF, EQ_POSITION, eq_group, G_N_ELEMENTS(eq_group)},
};
EffectList effects[] = {
{wah_effect, G_N_ELEMENTS(wah_effect)},
{amp_effect, G_N_ELEMENTS(amp_effect)},
{eq_effect, G_N_ELEMENTS(eq_effect)},
{comp_effect, G_N_ELEMENTS(comp_effect)},
{dist_effect, G_N_ELEMENTS(dist_effect)},

View File

@@ -14,6 +14,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses>.
*/
#ifndef GDIGI_EFFECTS_H
#define GDIGI_EFFECTS_H
#include <glib/gtypes.h>
typedef struct {
@@ -45,3 +48,5 @@ typedef struct {
Effect *effect; /* list of supported effects */
gint amt; /* list of supported effects length */
} EffectList;
#endif /* GDIGI_EFFECTS_H */

231
gdigi.c
View File

@@ -18,11 +18,12 @@
#include <gtk/gtk.h>
#include <getopt.h>
#include <alsa/asoundlib.h>
#include <string.h>
#include <alloca.h>
#include "gdigi.h"
#include "gui.h"
static snd_rawmidi_t *output;
static snd_rawmidi_t *output = NULL;
static snd_rawmidi_t *input = NULL;
static char *device = "hw:1,0,0";
/*
@@ -54,7 +55,7 @@ gboolean open_device()
{
int err;
err = snd_rawmidi_open(NULL, &output, device, SND_RAWMIDI_NONBLOCK);
err = snd_rawmidi_open(&input, &output, device, SND_RAWMIDI_NONBLOCK);
if (err) {
fprintf(stderr, "snd_rawmidi_open %s failed: %d\n", device, err);
return TRUE;
@@ -65,6 +66,9 @@ gboolean open_device()
fprintf(stderr, "snd_rawmidi_nonblock failed: %d\n", err);
return TRUE;
}
snd_rawmidi_read(input, NULL, 0); /* trigger reading */
return FALSE;
}
@@ -76,6 +80,68 @@ void send_data(char *data, int length)
snd_rawmidi_write(output, data, length);
}
/*
reads data from MIDI IN
returns GString containing data
if no data was read it returns NULL
*/
GString* read_data()
{
/* This is mostly taken straight from alsa-utils-1.0.19 amidi/amidi.c
by Clemens Ladisch <clemens@ladisch.de> */
int err;
int npfds;
struct pollfd *pfds;
GString *string = NULL;
npfds = snd_rawmidi_poll_descriptors_count(input);
pfds = alloca(npfds * sizeof(struct pollfd));
snd_rawmidi_poll_descriptors(input, pfds, npfds);
do {
char buf[20];
int i, length;
unsigned short revents;
err = poll(pfds, npfds, 200);
if (err < 0 && errno == EINTR)
break;
if (err < 0) {
g_error("poll failed: %s", strerror(errno));
break;
}
if ((err = snd_rawmidi_poll_descriptors_revents(input, pfds, npfds, &revents)) < 0) {
g_error("cannot get poll events: %s", snd_strerror(errno));
break;
}
if (revents & (POLLERR | POLLHUP))
break;
if (!(revents & POLLIN))
continue;
err = snd_rawmidi_read(input, buf, sizeof(buf));
if (err == -EAGAIN)
continue;
if (err < 0) {
g_error("cannot read: %s", snd_strerror(err));
break;
}
length = 0;
for (i = 0; i < err; ++i)
if (buf[i] != 0xFE) // ignore active sensing
buf[length++] = buf[i];
if (length != 0) {
if (string == NULL) {
string = g_string_new_len(buf, length);
} else {
string = g_string_append_len(string, buf, length);
}
}
} while (err != 0);
return string;
}
/*
id - ID as found in preset file
position - Position as found in preset file
@@ -151,21 +217,11 @@ void set_option(guint id, guint position, guint value)
}
/* x = 0 to 60 */
void switch_user_preset(int x)
void switch_preset(guint bank, guint x)
{
static char switch_preset[] = {0x00, 0xF0, 0x00, 0x00, 0x10, 0x00, 0x5E, 0x02, 0x39, 0x00, 0x01 /* bank = user */, 0x00 /* no */, 0x04, 0x00, 0x00, 0x01, 0x00 /* confirm */, 0xF7};
switch_preset[11] = x;
switch_preset[16] = calculate_checksum(switch_preset, sizeof(switch_preset), 16);
send_data(switch_preset, sizeof(switch_preset));
}
/* x = 0 to 60 */
void switch_system_preset(int x)
{
static char switch_preset[] = {0x00, 0xF0, 0x00, 0x00, 0x10, 0x00, 0x5E, 0x02, 0x39, 0x00, 0x00 /* bank = system */, 0x00 /* no */, 0x04, 0x00, 0x00, 0x01, 0x00 /* confirm */, 0xF7};
static char switch_preset[] = {0x00, 0xF0, 0x00, 0x00, 0x10, 0x00, 0x5E, 0x02, 0x39, 0x00, 0x00 /* bank */, 0x00 /* no */, 0x04, 0x00, 0x00, 0x01, 0x00 /* confirm */, 0xF7};
switch_preset[10] = bank;
switch_preset[11] = x;
switch_preset[16] = calculate_checksum(switch_preset, sizeof(switch_preset), 16);
@@ -178,6 +234,38 @@ void set_preset_level(int level)
set_option(PRESET_LEVEL, PRESET_POSITION, level);
}
void store_preset_name(int x, const gchar *name)
{
static char set_name[] = {0xF0, 0x00, 0x00, 0x10, 0x00, 0x5e, 0x02, 0x39, 0x00, 0x04, 0x00, 0x01, 0x00 /* preset number */, 0x00 /* name starts here */, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
set_name[12] = x;
int a;
int b;
b = 0;
for (a=0; (name != NULL && a<strlen(name)) && a<10 ; a++) {
if (a == 3) {
set_name[13+a] = 0x00;
b++;
}
set_name[13+a+b] = name[a];
}
if (a == 3) {
set_name[13+a+1+b] = 0x00;
a++;
} else
set_name[13+a+b] = 0x00;
set_name[13+a+1+b] = 0x00;
set_name[13+a+3+b] = 0xF7;
set_name[13+a+2+b] = calculate_checksum(set_name, 13+a+4+b, 13+a+2+b);
send_data(set_name, 14+a+3+b);
switch_preset(PRESETS_USER, x);
}
/* x = 0 to 59 (preset number) */
void set_preset_name(int x, gchar *name)
{
@@ -207,29 +295,100 @@ void set_preset_name(int x, gchar *name)
send_data(set_name, 13+a+3+b);
}
int main(int argc, char *argv[]) {
gtk_init(&argc, &argv);
/*
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
*/
GStrv query_preset_names(guint bank)
{
GString *data = NULL;
int x; /* used to iterate over whole reply */
int n = 0; /* current preset number */
int n_total; /* total number of presets */
gchar **str_array = NULL;
int c;
while (1) {
static struct option long_options[] = {
{"device", required_argument, 0, 'd'},
{0, 0, 0, 0}
/* clear MIDI IN buffer */
data = read_data();
if (data != NULL)
g_string_free(data, TRUE);
/* query user preset names */
char command[] = {0xF0, 0x00, 0x00, 0x10, 0x00, 0x5E, 0x02, 0x21, 0x00, 0x00 /* bank */, 0x00 /* checksum */, 0xF7};
command[9] = bank;
command[10] = calculate_checksum(command, sizeof(command), 10);
send_data(command, sizeof(command));
/* read reply */
data = read_data();
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[10];
str_array = g_new(gchar*, n_total + 1);
str_array[n_total] = NULL;
}
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!");
}
}
}
}
g_string_free(data, TRUE);
}
return str_array;
}
static GOptionEntry options[] = {
{"device", 'd', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_STRING, &device, "MIDI device port to use", NULL},
{NULL}
};
int option_index = 0;
c = getopt_long(argc, argv, "d:", long_options, &option_index);
if (c == -1)
break;
switch(c) {
case 'd':
device = optarg;
break;
default:
abort();
}
int main(int argc, char *argv[]) {
GError *error = NULL;
GOptionContext *context;
context = g_option_context_new(NULL);
g_option_context_add_main_entries(context, options, NULL);
g_option_context_add_group(context, gtk_get_option_group(TRUE));
if (!g_option_context_parse(context, &argc, &argv, &error)) {
g_message("option parsing failed: %s\n", error->message);
g_error_free(error);
g_option_context_free(context);
exit(EXIT_FAILURE);
}
g_option_context_free(context);
if (open_device() == TRUE) {
GtkWidget *msg = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR,
@@ -245,6 +404,8 @@ int main(int argc, char *argv[]) {
if (output != NULL)
snd_rawmidi_close(output);
if (input != NULL)
snd_rawmidi_close(input);
return 0;
return EXIT_SUCCESS;
}

43
gdigi.h
View File

@@ -14,7 +14,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses>.
*/
#ifndef GDIGI_H
#define GDIGI_H
#include <glib.h>
#include <glib-object.h>
enum {
WAH_TYPE_CRY = 132,
@@ -121,6 +125,31 @@ enum {
#define PRESET_POSITION 18
#define PRESET_LEVEL 2626
enum {
AMP_TYPE_TWEED_CHAMP = 307,
AMP_TYPE_TWEED_DELUXE = 308,
AMP_TYPE_TWEED_BASSMAN = 309,
AMP_TYPE_BLACKFACE_TWIN = 311,
AMP_TYPE_BLACKFACE_DELUXE = 312,
AMP_TYPE_SUPER_LEAD_PLEXI = 314,
AMP_TYPE_MASTER_VOLUME = 316,
AMP_TYPE_JCM800 = 317,
AMP_TYPE_JCM900 = 318,
AMP_TYPE_AC15 = 322,
AMP_TYPE_AC30TB = 323,
AMP_TYPE_HIWATT_100 = 324,
AMP_TYPE_BOOGIE_MARK_II = 320,
AMP_TYPE_DUAL_RECTIFIER = 321,
AMP_TYPE_MATCHLESS_HC30 = 326,
AMP_TYPE_SOLO = 331,
AMP_TYPE_METAL = 332,
AMP_TYPE_BRIGHT = 333,
AMP_TYPE_CLEAN = 335,
AMP_TYPE_HIGH_GAIN = 337,
AMP_TYPE_ACOUSTIC = 341,
AMP_TYPE_DIRECT = 306
};
enum {
EQ_TYPE_BRIGHT = 1474,
EQ_TYPE_MIDBOOST = 1472,
@@ -128,6 +157,8 @@ enum {
EQ_TYPE_WARM = 1475
};
#define AMP_TYPE 2496
#define AMP_ON_OFF 265
#define AMP_POSITION 8
#define AMP_GAIN 2497
@@ -458,7 +489,15 @@ enum {
#define USB_AUDIO_PLAYBACK_MIX 12297
#define USB_AUDIO_LEVEL 12307
typedef enum {
PRESETS_SYSTEM = 0,
PRESETS_USER = 1
} PresetBank;
void set_option(guint id, guint position, guint value);
void switch_user_preset(int x);
void switch_system_preset(int x);
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(PresetBank bank);
#endif /* GDIGI_H */

405
gui.c
View File

@@ -18,10 +18,21 @@
#include "gdigi.h"
#include "gui.h"
#include "effects.h"
#include "preset.h"
extern EffectList effects[];
extern int n_effects;
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)
{
g_return_if_fail(setting != NULL);
@@ -38,7 +49,21 @@ void toggled_cb(GtkToggleButton *button, Effect *effect)
set_option(effect->option, effect->position, val);
}
GtkWidget *create_table(EffectSettings *settings, gint amt)
static void widget_list_add(GList **list, GtkWidget *widget, gint id, gint position, gint value, gint x)
{
WidgetListElem *el;
el = g_malloc(sizeof(WidgetListElem));
el->widget = widget;
el->id = id;
el->position = position;
el->value = value;
el->x = x;
*list = g_list_prepend(*list, el);
}
GtkWidget *create_table(GList **list, EffectSettings *settings, gint amt)
{
GtkWidget *table, *label, *widget;
GtkObject *adj;
@@ -51,7 +76,7 @@ GtkWidget *create_table(EffectSettings *settings, gint amt)
adj = gtk_adjustment_new(0.0, settings[x].min, settings[x].max, 1.0, 1.0, 1.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);
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);
}
@@ -59,11 +84,12 @@ GtkWidget *create_table(EffectSettings *settings, gint amt)
return table;
}
GtkWidget *create_on_off_button(Effect *effect)
GtkWidget *create_on_off_button(GList **list, Effect *effect)
{
GtkWidget *button = gtk_toggle_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_list_add(list, button, effect->option, effect->position, -1, -1);
return button;
}
@@ -113,7 +139,7 @@ void combo_box_changed_cb(GtkComboBox *widget, gpointer data)
}
}
GtkWidget *create_widget_container(EffectGroup *group, gint amt)
GtkWidget *create_widget_container(GList **list, EffectGroup *group, gint amt)
{
GtkWidget *vbox;
GtkWidget *widget;
@@ -136,7 +162,7 @@ GtkWidget *create_widget_container(EffectGroup *group, gint amt)
gtk_combo_box_append_text(GTK_COMBO_BOX(combo_box), group[x].label);
cmbox_no++;
widget = create_table(group[x].settings, group[x].settings_amt);
widget = create_table(list, group[x].settings, group[x].settings_amt);
g_object_ref_sink(widget);
settings = g_malloc(sizeof(EffectSettingsGroup));
@@ -144,12 +170,13 @@ GtkWidget *create_widget_container(EffectGroup *group, gint amt)
settings->option = group[x].option;
settings->position = group[x].position;
settings->child = widget;
widget_list_add(list, combo_box, group[x].option, group[x].position, group[x].id, 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(group[x].settings, group[x].settings_amt);
widget = create_table(list, group[x].settings, group[x].settings_amt);
gtk_container_add(GTK_CONTAINER(vbox), widget);
}
}
@@ -157,7 +184,7 @@ GtkWidget *create_widget_container(EffectGroup *group, gint amt)
return vbox;
};
GtkWidget *create_vbox(Effect *widgets, gint amt)
GtkWidget *create_vbox(GList **list, Effect *widgets, gint amt)
{
GtkWidget *vbox;
GtkWidget *hbox;
@@ -171,10 +198,10 @@ GtkWidget *create_vbox(Effect *widgets, gint amt)
gtk_box_set_homogeneous(GTK_BOX(hbox), TRUE);
for (x = 0; x<amt; x++) {
widget = create_on_off_button(&widgets[x]);
widget = create_on_off_button(list, &widgets[x]);
gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 2);
table = create_widget_container(widgets[x].group, widgets[x].group_amt);
table = create_widget_container(list, widgets[x].group, widgets[x].group_amt);
gtk_box_pack_start(GTK_BOX(hbox), table, TRUE, TRUE, 2);
}
@@ -182,25 +209,381 @@ GtkWidget *create_vbox(Effect *widgets, gint amt)
return vbox;
}
enum {
PRESET_NAME_COLUMN = 0,
PRESET_NUMBER_COLUMN,
PRESET_BANK_COLUMN,
NUM_COLUMNS
};
void row_activate_cb(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *column, GtkTreeModel *model) {
GtkTreeIter iter;
gint id;
gint bank;
gtk_tree_model_get_iter(model, &iter, path);
gtk_tree_model_get(model, &iter, PRESET_NUMBER_COLUMN, &id, PRESET_BANK_COLUMN, &bank, -1);
if ((bank != -1) && (id != -1))
switch_preset(bank, id);
}
static void fill_store_with_presets(GtkTreeStore *model, guint bank, gchar *name)
{
GtkTreeIter iter;
GtkTreeIter child_iter;
int x;
GStrv presets = query_preset_names(bank);
gtk_tree_store_append(model, &iter, NULL);
gtk_tree_store_set(model, &iter,
PRESET_NAME_COLUMN, name,
PRESET_NUMBER_COLUMN, -1,
PRESET_BANK_COLUMN, -1,
-1);
for (x=0; x<g_strv_length(presets); x++) {
gtk_tree_store_append(model, &child_iter, &iter);
gtk_tree_store_set(model, &child_iter,
PRESET_NAME_COLUMN, presets[x],
PRESET_NUMBER_COLUMN, x,
PRESET_BANK_COLUMN, bank,
-1);
}
g_strfreev(presets);
}
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()
{
GtkWidget *treeview;
GtkTreeStore *store;
GtkCellRenderer *renderer;
store = gtk_tree_store_new(NUM_COLUMNS, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);
fill_store(store);
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
g_object_unref(store);
renderer = gtk_cell_renderer_text_new();
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview),
-1, "Preset name",
renderer, "text",
PRESET_NAME_COLUMN, NULL);
g_object_set(G_OBJECT(treeview), "headers-visible", FALSE, NULL);
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));
return treeview;
}
static void show_store_preset_window(GtkWidget *window, gchar *default_name)
{
GtkWidget *dialog, *cmbox, *entry, *table, *label;
GStrv names;
int x;
dialog = gtk_dialog_new_with_buttons("Store preset",
GTK_WINDOW(window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
NULL);
table = gtk_table_new(2, 2, FALSE);
gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), table);
cmbox = gtk_combo_box_new_text();
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);
g_free(title);
}
g_strfreev(names);
gtk_table_attach_defaults(GTK_TABLE(table), cmbox, 1, 2, 0, 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);
label = gtk_label_new("Preset slot:");
gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
label = gtk_label_new("Preset name:");
gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
gtk_widget_show_all(GTK_DIALOG(dialog)->vbox);
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
gint number = gtk_combo_box_get_active(GTK_COMBO_BOX(cmbox));
if (number != -1) {
store_preset_name(number, gtk_entry_get_text(GTK_ENTRY(entry)));
}
}
gtk_widget_destroy(dialog);
}
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 { /* combo box */
if (el->value == param->value)
gtk_combo_box_set_active(GTK_COMBO_BOX(el->widget), el->x);
}
}
}
static void apply_preset_to_gui(GList *list, Preset *preset)
{
GList *iter = preset->params;
while (iter) {
SettingParam *param = iter->data;
iter = iter->next;
if (param != NULL)
g_list_foreach(list, (GFunc)apply_widget_setting, param);
}
}
static void action_store_cb(GtkAction *action)
{
GtkWidget *window = g_object_get_data(G_OBJECT(action), "window");
show_store_preset_window(window, NULL);
}
static void action_show_about_dialog_cb(GtkAction *action)
{
static const gchar * const authors[] = {
"Tomasz Moń <desowin@gmail.com>",
NULL
};
static const gchar copyright[] = "Copyright \xc2\xa9 2009 Tomasz Moń";
static const gchar website[] = "http://desowin.org/gdigi/";
GtkWidget *window = g_object_get_data(G_OBJECT(action), "window");
gtk_show_about_dialog(GTK_WINDOW(window),
"authors", authors,
"copyright", copyright,
"website", website,
NULL);
}
typedef struct {
gchar *name;
gchar *suffix;
} SupportedFileTypes;
static SupportedFileTypes file_types[] = {
{"RP250Preset", "*.rp250p"},
};
static guint n_file_types = G_N_ELEMENTS(file_types);
static void action_open_preset_cb(GtkAction *action)
{
static GtkWidget *dialog = NULL;
if (dialog != NULL)
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,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
NULL);
GtkFileFilter *filter;
filter = gtk_file_filter_new();
gtk_file_filter_set_name(filter, "All Supported Types");
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
int x;
for (x=0; x<n_file_types; x++) {
GtkFileFilter *current_filter = gtk_file_filter_new();
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);
}
gboolean loaded = FALSE;
while (!loaded && gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
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);
gtk_widget_hide(dialog);
GList *iter = preset->params;
while (iter) {
SettingParam *param = iter->data;
iter = iter->next;
/* sending those is likely to freeze/reboot device */
if ((param->id == 8704) || (param->id == 8705))
continue;
set_option(param->id, param->position, param->value);
};
show_store_preset_window(window, preset->name);
preset_free(preset);
loaded = TRUE;
}
g_free(filename);
}
gtk_widget_destroy(dialog);
dialog = NULL;
}
static void widget_list_free(GList *list)
{
GList *iter;
for (iter = list; iter; iter = iter->next) {
g_free(iter->data);
}
g_list_free(list);
}
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();
}
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)},
{"About", GTK_STOCK_ABOUT, "_About", "<control>A", "About", G_CALLBACK(action_show_about_dialog_cb)},
};
static guint n_entries = G_N_ELEMENTS(entries);
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'/>"
" </menu>"
" <menu action='Help'>"
" <menuitem action='About'/>"
" </menu>"
" </menubar>"
"</ui>";
static void add_action_data(GtkUIManager *ui, const gchar *path, GtkWidget *window, GList **list)
{
GtkAction *action;
action = gtk_ui_manager_get_action(ui, path);
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)
{
GtkUIManager *ui;
GtkActionGroup *actions;
GError *error = NULL;
actions = gtk_action_group_new("Actions");
gtk_action_group_add_actions(actions, entries, n_entries, NULL);
ui = gtk_ui_manager_new();
gtk_ui_manager_insert_action_group(ui, actions, 0);
g_object_unref(actions);
gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui));
if (!gtk_ui_manager_add_ui_from_string(ui, menu_info, -1, &error)) {
g_message("building menus failed: %s", error->message);
g_error_free(error);
error = NULL;
}
gtk_box_pack_start(GTK_BOX(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);
g_object_unref(ui);
}
void create_window()
{
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);
gtk_window_set_title(GTK_WINDOW(window), "gdigi");
vbox = gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
add_menubar(&list, window, vbox);
hbox = gtk_hbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(vbox), hbox);
sw = gtk_scrolled_window_new(NULL, NULL);
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();
gtk_container_add(GTK_CONTAINER(sw), widget);
vbox = gtk_vbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 2);
for (x = 0; x<n_effects; x++) {
if ((x % 2) == 0) {
if ((x % 3) == 0) {
hbox = gtk_hbox_new(TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 2);
}
widget = create_vbox(effects[x].effect, effects[x].amt);
widget = create_vbox(&list, effects[x].effect, effects[x].amt);
gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 2);
}

5
gui.h
View File

@@ -14,4 +14,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses>.
*/
#ifndef GDIGI_GUI_H
#define GDIGI_GUI_H
void create_window();
#endif /* GDIGI_GUI_H */

181
preset.c Normal file
View File

@@ -0,0 +1,181 @@
/*
* 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 <string.h>
#include "preset.h"
enum {
PARSER_TYPE_NOT_SET = -1,
PARSER_TYPE_PRESET_NAME = 0,
PARSER_TYPE_PARAM_ID,
PARSER_TYPE_PARAM_POSITION,
PARSER_TYPE_PARAM_VALUE,
PARSER_TYPE_PARAM_NAME,
PARSER_TYPE_PARAM_TEXT
};
typedef struct {
int depth;
int id;
Preset *preset;
} AppData;
static void XMLCALL start(void *data, const char *el, const char **attr) {
AppData *ad = (AppData *) data;
ad->id = PARSER_TYPE_NOT_SET;
if (g_strcmp0(el, "Name") == 0) {
if (ad->depth == 1) {
ad->id = PARSER_TYPE_PRESET_NAME;
} else if (ad->depth == 3) {
ad->id = PARSER_TYPE_PARAM_NAME;
}
}
if (g_strcmp0(el, "Params") == 0) {
if (ad->preset->params != NULL)
g_message("Params aleady exists!");
} else if (g_strcmp0(el, "Param") == 0) {
SettingParam *param = (SettingParam *)g_malloc(sizeof(SettingParam));
param->id = -1;
param->position = -1;
param->value = -1;
ad->preset->params = g_list_prepend(ad->preset->params, param);
} else if (g_strcmp0(el, "ID") == 0) {
ad->id = PARSER_TYPE_PARAM_ID;
} else if (g_strcmp0(el, "Position") == 0) {
ad->id = PARSER_TYPE_PARAM_POSITION;
} else if (g_strcmp0(el, "Value") == 0) {
ad->id = PARSER_TYPE_PARAM_VALUE;
} else if (g_strcmp0(el, "Text") == 0) {
ad->id = PARSER_TYPE_PARAM_TEXT;
}
ad->depth++;
}
static void XMLCALL end(void *data, const char *el) {
AppData *ad = (AppData *) data;
ad->depth--;
ad->id = PARSER_TYPE_NOT_SET;
}
static void XMLCALL text_cb(void *data, const char* text, int len)
{
AppData *ad = (AppData *) data;
if ((ad == NULL) || (ad->preset == NULL))
return;
if (ad->id == PARSER_TYPE_PRESET_NAME) {
if (ad->preset->name != NULL)
g_free(ad->preset->name);
ad->preset->name = g_strndup(text, len);
}
if (ad->preset->params == NULL)
return;
SettingParam *param = (SettingParam *) ad->preset->params->data;
if (param == NULL)
return;
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;
}
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)
{
GFile *file;
GError *error = 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);
g_object_unref(file);
return NULL;
}
AppData *ad = (AppData *) g_malloc(sizeof(AppData));
ad->depth = 0;
ad->preset = g_malloc(sizeof(Preset));
ad->preset->name = NULL;
ad->preset->params = NULL;
ad->id = PARSER_TYPE_NOT_SET;
XML_Parser p;
p = XML_ParserCreate(NULL);
XML_SetUserData(p, (void *) ad);
XML_SetElementHandler(p, start, end);
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)));
preset_free(ad->preset);
g_free(ad);
g_free(contents);
g_object_unref(file);
return NULL;
}
Preset *preset = ad->preset;
preset->params = g_list_reverse(preset->params);
XML_ParserFree(p);
g_free(ad);
g_free(contents);
g_object_unref(file);
return 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);
g_list_free(preset->params);
}
if (preset->name != NULL)
g_free(preset->name);
g_free(preset);
}

36
preset.h Normal file
View File

@@ -0,0 +1,36 @@
/*
* 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_PRESET_H
#define GDIGI_PRESET_H
#include <glib.h>
typedef struct {
int id;
int position;
int value;
} SettingParam;
typedef struct {
gchar *name;
GList *params;
} Preset;
Preset *create_preset_from_xml_file(gchar *filename);
void preset_free(Preset *preset);
#endif /* GDIGI_PRESET_H */

View File

@@ -206,10 +206,10 @@ void test_presets()
int x;
for (x=0; x<=60; x++)
switch_user_preset(x);
switch_preset(PRESETS_USER, x);
for (x=0; x<=60; x++)
switch_system_preset(x);
switch_preset(PRESETS_SYSTEM, x);
for (x=0; x<=99; x++)
set_preset_level(x);

View File

@@ -14,6 +14,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses>.
*/
#ifndef GDIGI_TESTS_H
#define GDIGI_TESTS_H
#include "gdigi.h"
void test_wah();
@@ -29,3 +32,5 @@ void test_reverb();
void test_exp();
void test_usb();
void test_all();
#endif /* GDIGI_TESTS_H */