/[pidim]/src/pidim_split_chan.c
ViewVC logotype

Contents of /src/pidim_split_chan.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 20 - (show annotations)
Fri Aug 27 00:11:30 2010 UTC (10 years, 9 months ago) by ben
File MIME type: text/plain
File size: 9450 byte(s)
factorised cli code
1 /* pidim_split_chan, a small Gtk+/ALSA MIDI keyboard splitter over two chans
2 * Copyright (C) 2010 Benoit Rouits
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <libintl.h>
24 #include <glib.h>
25 #include <gtk/gtk.h>
26
27 #include <stdlib.h>
28 #include <alsa/asoundlib.h>
29 #include "pidim_common.h"
30
31 /* alsa-related type */
32 /* this application has one input and one output port */
33 typedef struct {
34 snd_seq_t* handler; /* sequencer handler */
35 int inport_id; /* id of input port */
36 int outport_id; /* id of output port */
37 } alsa_t;
38
39 /* globals */
40 GtkBuilder* ui = NULL;
41 int Splitnote = 0;
42 int Controller = 0;
43 int Channel = 0;
44 int Upchan = 0;
45 int Lowchan = 0;
46 char Instance[64];
47
48 /* Gtk stuff */
49 void ui_new (void)
50 {
51 gchar* ui_path = NULL;
52 GtkWidget *split_spinbutton, *controller_spinbutton, *channel_spinbutton, *upchan_spinbutton, *lowchan_spinbutton;
53
54 if (g_file_test("./pidim_split_chan.ui", G_FILE_TEST_EXISTS))
55 ui_path = "./pidim_split_chan.ui";
56 else
57 ui_path = PACKAGE_DATA_DIR"/pidim/ui/pidim_split_chan.ui";
58
59 ui = gtk_builder_new();
60 gtk_builder_add_from_file(ui, ui_path, NULL);
61
62 split_spinbutton = GTK_WIDGET(gtk_builder_get_object(ui, "split_spinbutton"));
63 gtk_spin_button_set_range(GTK_SPIN_BUTTON(split_spinbutton), 0, VAL_MAX);
64 gtk_spin_button_set_increments(GTK_SPIN_BUTTON(split_spinbutton), 1, 0);
65
66 upchan_spinbutton = GTK_WIDGET(gtk_builder_get_object(ui, "upchan_spinbutton"));
67 gtk_spin_button_set_range(GTK_SPIN_BUTTON(upchan_spinbutton), 0, CHAN_MAX);
68 gtk_spin_button_set_increments(GTK_SPIN_BUTTON(upchan_spinbutton), 1, 0);
69
70 lowchan_spinbutton = GTK_WIDGET(gtk_builder_get_object(ui, "lowchan_spinbutton"));
71 gtk_spin_button_set_range(GTK_SPIN_BUTTON(lowchan_spinbutton), 0, CHAN_MAX);
72 gtk_spin_button_set_increments(GTK_SPIN_BUTTON(lowchan_spinbutton), 1, 0);
73
74 controller_spinbutton = GTK_WIDGET(gtk_builder_get_object(ui, "controller_spinbutton"));
75 gtk_spin_button_set_range(GTK_SPIN_BUTTON(controller_spinbutton), 0, CTRL_MAX);
76 gtk_spin_button_set_increments(GTK_SPIN_BUTTON(controller_spinbutton), 1, 0);
77
78 channel_spinbutton = GTK_WIDGET(gtk_builder_get_object(ui, "channel_spinbutton"));
79 gtk_spin_button_set_range(GTK_SPIN_BUTTON(channel_spinbutton), 0, CHAN_MAX);
80 gtk_spin_button_set_increments(GTK_SPIN_BUTTON(channel_spinbutton), 1, 0);
81
82 gtk_builder_connect_signals(ui, NULL);
83 gtk_window_set_default_icon_from_file(PACKAGE_DATA_DIR"/pidim/ui/pidim.png", NULL);
84 }
85
86 void ui_show()
87 {
88 GtkWidget* main_window = GTK_WIDGET(gtk_builder_get_object(ui, "main_window"));
89 gtk_window_set_title(GTK_WINDOW(main_window), Instance);
90 gtk_widget_show_all(main_window);
91
92 }
93 gboolean on_main_window_delete_event(GtkWidget* main_window, GdkEvent* null1, gpointer null2) {
94 null1 = NULL;
95 null2 = NULL;
96 gtk_main_quit();
97 return TRUE;
98 }
99
100 /* callback called when the user interacts on split spin button */
101 void on_split_spinbutton_value_changed(GtkSpinButton* spin_button, gpointer* null) {
102 Splitnote = (int) gtk_spin_button_get_value_as_int(spin_button);
103 null = NULL;
104 }
105
106 /* callback called when the user interacts on upchan spin button */
107 void on_upchan_spinbutton_value_changed(GtkSpinButton* spin_button, gpointer* null) {
108 Upchan = (int) gtk_spin_button_get_value_as_int(spin_button);
109 null = NULL;
110 }
111
112 /* callback called when the user interacts on lowchan spin button */
113 void on_lowchan_spinbutton_value_changed(GtkSpinButton* spin_button, gpointer* null) {
114 Lowchan = (int) gtk_spin_button_get_value_as_int(spin_button);
115 null = NULL;
116 }
117
118 /* callback called when the user interacts on controller spin button */
119 void on_controller_spinbutton_value_changed(GtkSpinButton* spin_button, gpointer* null)
120 {
121 Controller = (int) gtk_spin_button_get_value_as_int(spin_button);
122 null = NULL;
123 }
124
125 /* callback called when the user interacts on channel spin button */
126 void on_channel_spinbutton_value_changed(GtkSpinButton* spin_button, gpointer* null) {
127 Channel = (int) gtk_spin_button_get_value_as_int(spin_button);
128 null = NULL;
129 }
130
131 /* split operation on midi events */
132 void do_split (snd_seq_event_t* ev, alsa_t* alsa, int point)
133 {
134 GtkWidget* split_spinbutton;
135 int i;
136 snd_seq_event_t dup;
137 /* stuff needed to not loose matching ON/OFF even if 'point' has changed between note completion */
138 struct mem_t {
139 unsigned char channel;
140 unsigned char note;
141 int split;
142 };
143 static struct mem_t* memory = NULL;
144 static int memory_len = 0;
145
146 if ((ev->type == SND_SEQ_EVENT_NOTEON) && (ev->data.note.velocity == 0))
147 ev->type = SND_SEQ_EVENT_NOTEOFF;
148
149 switch (ev->type) {
150 case SND_SEQ_EVENT_CONTROLLER:
151 if (!Channel || ev->data.control.channel == Channel) {
152 if (ev->data.control.param == Controller) {
153 point = Splitnote = ev->data.control.value;
154 split_spinbutton = GTK_WIDGET(gtk_builder_get_object(ui, "split_spinbutton"));
155 gtk_spin_button_set_value(GTK_SPIN_BUTTON(split_spinbutton), Splitnote);
156 }
157 }
158 /* duplicate controller event on the two channels (out to same port) */
159 snd_seq_ev_clear(&dup);
160 memcpy(&dup, ev, sizeof(dup));
161
162 ev->data.control.channel = Lowchan;
163 dup.data.control.channel = Upchan;
164
165 snd_seq_ev_set_source(ev, alsa->outport_id);
166 snd_seq_ev_set_source(&dup, alsa->outport_id);
167
168 snd_seq_ev_set_subs(ev);
169 snd_seq_ev_set_subs(&dup);
170
171 snd_seq_ev_set_direct(ev);
172 snd_seq_ev_set_direct(&dup);
173
174 snd_seq_event_output(alsa->handler, ev);
175 snd_seq_event_output(alsa->handler, &dup);
176
177 snd_seq_drain_output(alsa->handler);
178 break;
179 case SND_SEQ_EVENT_NOTEON:
180 if (!Channel || ev->data.note.channel == Channel) {
181 i = 0;
182 while (i < memory_len && memory[i].note != '\0') {
183 ++i;
184 }
185 if (i >= memory_len) {
186 memory = (struct mem_t*) realloc(memory, (memory_len + 1)*sizeof(struct mem_t));
187 memory_len += 1;
188 }
189 /* memorize the note and its transposition value */
190 memory[i].channel = ev->data.note.channel;
191 memory[i].note = ev->data.note.note;
192 memory[i].split = point;
193 /* apply splitting */
194 if (ev->data.note.note < Splitnote) {
195 ev->data.note.channel = Lowchan;
196 } else {
197 ev->data.note.channel = Upchan;
198 }
199 }
200 snd_seq_ev_set_source(ev, alsa->outport_id);
201 snd_seq_ev_set_subs(ev);
202 snd_seq_event_output_direct(alsa->handler, ev);
203 break;
204 case SND_SEQ_EVENT_NOTEOFF:
205 if (!Channel || ev->data.note.channel == Channel) {
206 i = 0;
207 /* search matching note in memory */
208 while (i < memory_len && \
209 (memory[i].channel != ev->data.note.channel || \
210 memory[i].note != ev->data.note.note)) {
211 ++i;
212 }
213 if (i >= memory_len) {
214 /* should not append */
215 /* do nothing */
216 } else {
217 /* apply memorized split */
218 if (memory[i].note == ev->data.note.note) {
219 if (ev->data.note.note < memory[i].split) {
220 ev->data.note.channel = Lowchan;
221 } else {
222 ev->data.note.channel = Upchan;
223 }
224 }
225 /* then forget */
226 memory[i].note = '\0';
227 }
228 }
229 snd_seq_ev_set_source(ev, alsa->outport_id);
230 snd_seq_ev_set_subs(ev);
231 snd_seq_event_output_direct(alsa->handler, ev);
232 break;
233 }
234 return;
235 }
236
237 /* MIDI input callback */
238 gboolean handle_midi_input (GIOChannel* midi_input, GIOCondition cond, gpointer user_data)
239 {
240 snd_seq_event_t* ev;
241 alsa_t* alsa = (alsa_t*) user_data;
242 do {
243 snd_seq_event_input(alsa->handler, &ev);
244 do_split(ev, alsa, Splitnote);
245 } while (snd_seq_event_input_pending(alsa->handler, 0) > 0);
246 cond = 0;
247 midi_input = NULL;
248 return TRUE;
249 }
250
251 /* starting point */
252 int main (int argc, char* argv[])
253 {
254 char* argv1;
255 GIOChannel* midi_input;
256 int alsa_input_fd;
257 alsa_t alsa;
258
259 /* initialize language translation */
260 #ifdef ENABLE_NLS
261 bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
262 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
263 textdomain (GETTEXT_PACKAGE);
264 #endif
265 /* initialize gtk+ */
266 gtk_set_locale();
267 gtk_init(&argc, &argv);
268
269 memset(Instance, '\0', 1);
270 strncat(Instance, "PIDIM Split Chan ", 64);
271
272 /* read extra parameters */
273 argv1 = cli_parse(argc, argv, "A Keyboard Splitter over 2 channels.", \
274 "Enter the split point note number and set the output channels to control 2 instruments with one keyboard.");
275 if (argv1) {
276 strncat(Instance, argv1, 48);
277 }
278
279 /* create window */
280 ui_new();
281 ui_show();
282
283 /* initialize alsa stuff */
284 alsa.handler = alsa_open_client(Instance);
285 if (!alsa.handler) {
286 exit(EXIT_FAILURE);
287 }
288 alsa.inport_id = alsa_open_input_port(alsa.handler, "IN");
289 alsa.outport_id = alsa_open_output_port(alsa.handler, "OUT");
290 alsa_input_fd = alsa_get_fd(alsa.handler);
291 midi_input = g_io_channel_unix_new(alsa_input_fd);
292 g_io_add_watch(midi_input, G_IO_IN, handle_midi_input, &alsa);
293
294 gtk_main();
295 exit(EXIT_SUCCESS);
296 }
297

  ViewVC Help
Powered by ViewVC 1.1.26