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

Contents of /src/pidim_arpeggiate.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: 12611 byte(s)
factorised cli code
1 /* pidim_arpeggiate, a Gtk+/ALSA MIDI arpeggiator
2 * Copyright (C) 2010 Benoit Rouits
3 * Based on miniArp.c by Matthias Nagorni
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <libintl.h>
25 #include <glib.h>
26 #include <gtk/gtk.h>
27
28 #include <stdlib.h>
29 #include <ctype.h>
30 #include <alsa/asoundlib.h>
31 #include "pidim_common.h"
32
33 #define BPM0 240 /* default tempo */
34 #define TICKS_PER_QUARTER 128 /* precision */
35 #define MAX_SEQ_LEN 64 /* maximum number of notes in arpeggio */
36
37 /* alsa-related type */
38 /* this application has one input and one output port */
39 typedef struct {
40 snd_seq_t* handler; /* sequencer handler */
41 int sequence[3][MAX_SEQ_LEN]; /* tick, duration, transposition (semitones) */
42 int seq_len; /* sequence length */
43 int velocity; /* sequence velocity, based on input note */
44 int channel; /* sequence channel, based on input note */
45 int transpose; /* arpeggio transposition */
46 int queue_id; /* arpeggio event queue */
47 snd_seq_tick_time_t tick; /* queue_id tick */
48 int swing; /* tempo modulation */
49 int inport_id; /* id of input port 0 */
50 int outport_id; /* id of output port 0 */
51 } alsa_t;
52
53 /* globals */
54 GtkBuilder* ui = NULL;
55 int Bpm0 = BPM0;
56 int Bpm = BPM0;
57 int Controller = 0;
58 int Channel = 0;
59 char Instance[64];
60
61 /* Gtk stuff */
62 void ui_new (void)
63 {
64 gchar* ui_path = NULL;
65 GtkWidget *bpm_spinbutton, *controller_spinbutton, *channel_spinbutton;
66
67 if (g_file_test("./pidim_arpeggiate.ui", G_FILE_TEST_EXISTS))
68 ui_path = "./pidim_arpeggiate.ui";
69 else
70 ui_path = PACKAGE_DATA_DIR"/pidim/ui/pidim_arpeggiate.ui";
71
72 ui = gtk_builder_new();
73 gtk_builder_add_from_file(ui, ui_path, NULL);
74
75 bpm_spinbutton = GTK_WIDGET(gtk_builder_get_object(ui, "bpm_spinbutton"));
76 gtk_spin_button_set_range(GTK_SPIN_BUTTON(bpm_spinbutton), 0, 511);
77 gtk_spin_button_set_increments(GTK_SPIN_BUTTON(bpm_spinbutton), 1, 0);
78 gtk_spin_button_set_value(GTK_SPIN_BUTTON(bpm_spinbutton), Bpm0);
79
80 controller_spinbutton = GTK_WIDGET(gtk_builder_get_object(ui, "controller_spinbutton"));
81 gtk_spin_button_set_range(GTK_SPIN_BUTTON(controller_spinbutton), 0, CTRL_MAX);
82 gtk_spin_button_set_increments(GTK_SPIN_BUTTON(controller_spinbutton), 1, 0);
83
84 channel_spinbutton = GTK_WIDGET(gtk_builder_get_object(ui, "channel_spinbutton"));
85 gtk_spin_button_set_range(GTK_SPIN_BUTTON(channel_spinbutton), 0, CHAN_MAX);
86 gtk_spin_button_set_increments(GTK_SPIN_BUTTON(channel_spinbutton), 1, 0);
87
88 gtk_window_set_default_icon_from_file(PACKAGE_DATA_DIR"/pidim/ui/pidim.png", NULL);
89 }
90
91 void ui_show()
92 {
93 GtkWidget* main_window = GTK_WIDGET(gtk_builder_get_object(ui, "main_window"));
94 gtk_window_set_title(GTK_WINDOW(main_window), Instance);
95 gtk_widget_show_all(main_window);
96
97 }
98
99 /* callback called when the user closes the window througth the window manager */
100 gboolean on_main_window_delete_event(GtkWidget* main_window, GdkEvent* null1, gpointer null2) {
101 null1 = NULL;
102 null2 = NULL;
103 gtk_main_quit();
104 return TRUE;
105 }
106
107 /* set tempo of arpeggio queue */
108 void set_tempo(alsa_t* alsa)
109 {
110 snd_seq_queue_tempo_t *queue_tempo;
111 int tempo;
112
113 snd_seq_queue_tempo_malloc(&queue_tempo);
114 tempo = (int)(6e7 / ((double)Bpm * (double)TICKS_PER_QUARTER) * (double)TICKS_PER_QUARTER);
115 snd_seq_queue_tempo_set_tempo(queue_tempo, tempo);
116 snd_seq_queue_tempo_set_ppq(queue_tempo, TICKS_PER_QUARTER);
117 snd_seq_set_queue_tempo(alsa->handler, alsa->queue_id, queue_tempo);
118 snd_seq_queue_tempo_free(queue_tempo);
119 }
120
121 /* generate arpegio */
122 void arpeggio(alsa_t* alsa) {
123
124 snd_seq_event_t ev;
125 int l1; /* index in sequence */
126 double dt; /* delay between notes */
127
128 for (l1 = 0; l1 < alsa->seq_len; l1++) {
129 dt = (l1 % 2 == 0) ? (double)alsa->swing / 16384.0 : -(double)alsa->swing / 16384.0;
130 snd_seq_ev_clear(&ev);
131 snd_seq_ev_set_note(&ev, alsa->channel, alsa->sequence[2][l1] + alsa->transpose, alsa->velocity, alsa->sequence[1][l1]);
132 snd_seq_ev_schedule_tick(&ev, alsa->queue_id, 0, alsa->tick);
133 snd_seq_ev_set_source(&ev, alsa->outport_id);
134 snd_seq_ev_set_subs(&ev);
135 snd_seq_event_output_direct(alsa->handler, &ev);
136 alsa->tick += (int)((double)alsa->sequence[0][l1] * (1.0 + dt));
137 }
138 snd_seq_ev_clear(&ev);
139 ev.type = SND_SEQ_EVENT_ECHO;
140 snd_seq_ev_schedule_tick(&ev, alsa->queue_id, 0, alsa->tick);
141 snd_seq_ev_set_dest(&ev, snd_seq_client_id(alsa->handler), alsa->inport_id);
142 snd_seq_event_output_direct(alsa->handler, &ev);
143 }
144
145
146 /* callback called when the user changed the arpeggio sequence */
147 void on_arpeggio_entry_activate (GtkEntry* entry, gpointer* user_data)
148 {
149 enum {NONE, ALTER, KEY, OCTAVA, DURATION} state;
150 int i = -1;
151 int l1 = 0;
152 const gchar* text = gtk_entry_get_text(entry);
153 int len = gtk_entry_get_text_length(entry);
154 alsa_t* alsa = (alsa_t*) user_data;
155
156 for (i = 0; i < 3; i++)
157 for (l1 = 0; l1 < MAX_SEQ_LEN; l1++)
158 alsa->sequence[i][l1] = 0;
159 i = -1;
160 l1 = 0;
161 state = NONE;
162 while ((i < len) && (l1 < MAX_SEQ_LEN)) {
163 i++;
164 if (i == len) {
165 state = NONE;
166 continue;
167 }
168 /* parse note key, "à la ABC" */
169 /* a correct sentence is e.g. "C1 ^F1 G2 c2 c'1" */
170 if (text[i] == '^' && state == NONE) { /* optional sharp */
171 state = ALTER;
172 alsa->sequence[2][l1] += 1;
173 continue;
174 }
175 if (text[i] == '_' && state == NONE) { /* optional flat */
176 state = ALTER;
177 alsa->sequence[2][l1] -= 1;
178 continue;
179 }
180 if ((state == NONE || state == ALTER) && isalpha(text[i])) {
181 switch (text[i]) { /* mandatory note name */
182 case 'C': alsa->sequence[2][l1] += 0; state = KEY; break;
183 case 'D': alsa->sequence[2][l1] += 2; state = KEY; break;
184 case 'E': alsa->sequence[2][l1] += 4; state = KEY; break;
185 case 'F': alsa->sequence[2][l1] += 5; state = KEY; break;
186 case 'G': alsa->sequence[2][l1] += 7; state = KEY; break;
187 case 'A': alsa->sequence[2][l1] += 9; state = KEY; break;
188 case 'B': alsa->sequence[2][l1] += 11; state = KEY; break;
189 case 'c': alsa->sequence[2][l1] += 12; state = KEY; break;
190 case 'd': alsa->sequence[2][l1] += 14; state = KEY; break;
191 case 'e': alsa->sequence[2][l1] += 16; state = KEY; break;
192 case 'f': alsa->sequence[2][l1] += 17; state = KEY; break;
193 case 'g': alsa->sequence[2][l1] += 19; state = KEY; break;
194 case 'a': alsa->sequence[2][l1] += 21; state = KEY; break;
195 case 'b': alsa->sequence[2][l1] += 23; state = KEY; break;
196 default: break;
197 }
198 continue;
199 }
200 if (text[i] == '\'' && state == KEY) { /* optional octava up */
201 alsa->sequence[2][l1] += 12;
202 state = OCTAVA;
203 continue;
204 }
205 if (text[i] == ',' && state == KEY) { /* optional octava bassa */
206 alsa->sequence[2][l1] -= 12;
207 state = OCTAVA;
208 continue;
209 }
210 if ((state == OCTAVA || state == KEY) && isdigit(text[i])) {
211 /* parse duration */
212 alsa->sequence[1][l1] = TICKS_PER_QUARTER * (text[i] - '0');
213 alsa->sequence[0][l1] = TICKS_PER_QUARTER * (text[i] - '0') * 0.75;
214 state = DURATION;
215 continue;
216 }
217 if (state == DURATION && (text[i] == ' ' || text[i] == '\n' || text[i] == '\r')) {
218 /* parse separator */
219 state = NONE;
220 l1++;
221 continue;
222 }
223
224 } /* end of sequence parsing */
225 alsa->seq_len = l1;
226 if (alsa->seq_len) {
227 alsa->queue_id = alsa_init_queue(alsa->handler, alsa->seq_len);
228 set_tempo(alsa);
229 arpeggio(alsa);
230 snd_seq_start_queue(alsa->handler, alsa->queue_id, NULL);
231 snd_seq_drain_output(alsa->handler);
232 }
233 }
234
235 /* callback called when the user interacts on bpm spin button */
236 void on_bpm_spinbutton_value_changed(GtkSpinButton* spin_button, gpointer* user_data)
237 {
238 alsa_t* alsa = (alsa_t*) user_data;
239 Bpm0 = (int) gtk_spin_button_get_value_as_int(spin_button);
240 Bpm = Bpm0;
241 set_tempo(alsa);
242 }
243
244 /* callback called when the user interacts on controller spin button */
245 void on_controller_spinbutton_value_changed(GtkSpinButton* spin_button, gpointer* null)
246 {
247 Controller = (unsigned char) gtk_spin_button_get_value_as_int(spin_button);
248 null = NULL;
249 }
250
251 /* callback called when the user interacts on channel spin button */
252 void on_channel_spinbutton_value_changed(GtkSpinButton* spin_button, gpointer* null) {
253 Channel = (int) gtk_spin_button_get_value_as_int(spin_button);
254 null = NULL;
255 }
256
257 /* return the current tick */
258 snd_seq_tick_time_t get_tick(alsa_t* alsa) {
259
260 snd_seq_queue_status_t *status;
261 snd_seq_tick_time_t current_tick;
262
263 snd_seq_queue_status_malloc(&status);
264 snd_seq_get_queue_status(alsa->handler, alsa->queue_id, status);
265 current_tick = snd_seq_queue_status_get_tick_time(status);
266 snd_seq_queue_status_free(status);
267 return(current_tick);
268 }
269
270 /* arpeggiate operation on midi events */
271 void do_arpeggiate (snd_seq_event_t* ev, alsa_t* alsa)
272 {
273 #if 0
274 GtkWidget* bpm_spinbutton;
275 #endif
276 if ((ev->type == SND_SEQ_EVENT_NOTEON) && (ev->data.note.velocity == 0))
277 ev->type = SND_SEQ_EVENT_NOTEOFF;
278
279 switch (ev->type) {
280 case SND_SEQ_EVENT_CONTROLLER:
281 if (!Channel || ev->data.control.channel == Channel) {
282 if (ev->data.control.param == Controller) {
283 Bpm = (int)((double)Bpm0 * (1.0 + ((double)ev->data.control.value / 127.0) - 0.5));
284 set_tempo(alsa);
285 #if 0 /* Do not update the spinbutton beacause this yields to call the "changed" callback */
286 bpm_spinbutton = GTK_WIDGET(gtk_builder_get_object(ui, "bpm_spinbutton"));
287 gtk_spin_button_set_value(GTK_SPIN_BUTTON(bpm_spinbutton), Bpm);
288 #endif
289
290 }
291 }
292 break;
293 case SND_SEQ_EVENT_PITCHBEND:
294 alsa->swing = (double)ev->data.control.value;
295 break;
296 case SND_SEQ_EVENT_ECHO:
297 arpeggio(alsa);
298 break;
299 case SND_SEQ_EVENT_NOTEON:
300 if (!Channel || ev->data.note.channel == Channel) {
301 alsa_clear_queue(alsa->handler, alsa->queue_id);
302 alsa->transpose = ev->data.note.note;
303 alsa->tick = get_tick(alsa);
304 alsa->velocity = ev->data.note.velocity;
305 alsa->channel = ev->data.note.channel;
306 arpeggio(alsa);
307 }
308 break;
309 }
310 return;
311 }
312
313 /* MIDI input callback */
314 gboolean handle_midi_input (GIOChannel* midi_input, GIOCondition cond, gpointer user_data)
315 {
316 snd_seq_event_t* ev;
317 alsa_t* alsa = (alsa_t*) user_data;
318 do {
319 snd_seq_event_input(alsa->handler, &ev);
320 do_arpeggiate(ev, alsa);
321 snd_seq_free_event(ev);
322 } while (snd_seq_event_input_pending(alsa->handler, 0) > 0);
323 cond = 0;
324 midi_input = NULL;
325 return TRUE;
326 }
327
328 /* starting point */
329 int main (int argc, char* argv[])
330 {
331 char* argv1;
332 GIOChannel* midi_input;
333 int alsa_input_fd;
334 alsa_t alsa;
335
336 alsa.handler = NULL;
337 alsa.seq_len = 0;
338 alsa.velocity = 0;
339 alsa.channel = 0;
340 alsa.transpose = 0;
341 alsa.queue_id = 0;
342 alsa.tick = 0;
343 alsa.swing = 0;
344 alsa.inport_id = 0;
345 alsa.outport_id = 0;
346
347 /* initialize language translation */
348 #ifdef ENABLE_NLS
349 bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
350 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
351 textdomain (GETTEXT_PACKAGE);
352 #endif
353 /* initialize gtk+ */
354 gtk_set_locale();
355 gtk_init(&argc, &argv);
356
357 memset(Instance, '\0', 1);
358 strncat(Instance, "PIDIM Arpeggiate ", 64);
359
360 /* read extra parameters */
361 argv1 = cli_parse(argc, argv, "A MIDI arpeggiator.", \
362 "Enter the arpeggio in ABC syntax, with a trailing space. 'C' is the reference input note. Press [ENTER] to activate the arpeggio. Example of arpeggio: \"C1 G1 E1 c1 \"");
363
364 if (argv1) {
365 strncat(Instance, argv1, 48);
366 }
367
368 /* create window */
369 ui_new();
370 gtk_builder_connect_signals(ui, &alsa);
371 ui_show();
372
373 /* initialize alsa stuff */
374 alsa.handler = alsa_open_client(Instance);
375 if (!alsa.handler) {
376 exit(EXIT_FAILURE);
377 }
378 alsa.inport_id = alsa_open_input_port(alsa.handler, "IN");
379 alsa.outport_id = alsa_open_output_port(alsa.handler, "OUT");
380 alsa_input_fd = alsa_get_fd(alsa.handler);
381 midi_input = g_io_channel_unix_new(alsa_input_fd);
382 g_io_add_watch(midi_input, G_IO_IN, handle_midi_input, &alsa);
383
384 gtk_main();
385 exit(EXIT_SUCCESS);
386 }
387

  ViewVC Help
Powered by ViewVC 1.1.26