2
terminatorX - realtime audio scratching software
3
Copyright (C) 2002 Arthur Peters
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.
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.
15
You should have received a copy of the GNU General Public License
16
along with this program; if not, write to the Free Software
17
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
Description: Implements MIDI input to control turntable parameters.
23
Changes (Alexander K�nig <alex@lisas.de>:
24
- Using a glib GIOCallback instead of polling events
25
- Updating the treeview immedialtey after bind/unbind_clicked
26
- Adding "remove binding" option
27
- Adding destroy handler for the GUI
28
- moving printf to tX_* macros
29
- removing some debug code
31
- re-connect to MIDI devices
35
#include "tX_midiin.h"
37
#include "tX_glade_interface.h"
38
#include "tX_glade_support.h"
39
#include "tX_dialog.h"
40
#include "tX_mastergui.h"
42
#ifdef USE_ALSA_MIDI_IN
43
#include "tX_global.h"
45
#include "tX_engine.h"
49
static gboolean midi_callback(GIOChannel *source, GIOCondition condition, gpointer data) {
50
tX_midiin *midi=(tX_midiin *) data;
56
tX_midiin::tX_midiin()
62
if (snd_seq_open(&ALSASeqHandle, "default", SND_SEQ_OPEN_INPUT, 0) < 0) {
63
tX_error("tX_midiin(): failed to open the default sequencer device.");
66
snd_seq_set_client_name(ALSASeqHandle, "terminatorX");
68
snd_seq_create_simple_port(ALSASeqHandle,
70
SND_SEQ_PORT_CAP_WRITE
71
| SND_SEQ_PORT_CAP_SUBS_WRITE,
72
SND_SEQ_PORT_TYPE_APPLICATION);
74
tX_error("tX_midiin(): error creating sequencer port.");
78
snd_seq_nonblock( ALSASeqHandle, 1 );
80
struct pollfd fds[32];
82
int res=snd_seq_poll_descriptors (ALSASeqHandle, fds, 32, POLLIN);
85
tX_error("Failed to poll ALSA descriptors: %i.\n", res);
88
GIOChannel *ioc=g_io_channel_unix_new(fds[0].fd);
89
g_io_add_watch(ioc, (GIOCondition)( G_IO_IN ), midi_callback, (gpointer) this);
90
g_io_channel_unref(ioc);
94
tX_debug("tX_midiin(): sequencer successfully opened.");
97
tX_midiin::~tX_midiin()
100
snd_seq_close(ALSASeqHandle);
101
tX_debug("tX_midiin(): sequencer closed.");
105
int tX_midiin::check_event()
109
while( snd_seq_event_input(ALSASeqHandle, &ev) != -EAGAIN )
112
//MidiEvent::type MessageType=MidiEvent::NONE;
113
//int Volume=0,Note=0,EventDevice=0;
115
event.is_noteon = false;
116
bool event_usable = true;
119
case SND_SEQ_EVENT_CONTROLLER:
120
event.type = tX_midievent::CC;
121
event.number = ev->data.control.param;
122
event.value = ev->data.control.value / 127.0;
123
event.channel = ev->data.control.channel;
125
case SND_SEQ_EVENT_PITCHBEND:
126
event.type = tX_midievent::PITCHBEND;
127
event.number = ev->data.control.param;
128
event.value = (ev->data.control.value + 8191.0) / 16382.0; // 127.0;
129
event.channel = ev->data.control.channel;
131
case SND_SEQ_EVENT_CONTROL14:
132
event.type = tX_midievent::CC14;
133
event.number = ev->data.control.param;
134
event.value = ev->data.control.value / 16383.0;
135
event.channel = ev->data.control.channel;
137
case SND_SEQ_EVENT_REGPARAM:
138
event.type = tX_midievent::RPN;
139
event.number = ev->data.control.param;
140
event.value = ev->data.control.value / 16383.0;
141
event.channel = ev->data.control.channel;
143
case SND_SEQ_EVENT_NONREGPARAM:
144
event.type = tX_midievent::NRPN;
145
event.number = ev->data.control.param;
146
event.value = ev->data.control.value / 16383.0;
147
event.channel = ev->data.control.channel;
149
case SND_SEQ_EVENT_NOTEON:
150
event.type = tX_midievent::NOTE;
151
event.number = ev->data.note.note;
152
event.value = ev->data.note.velocity / 127.0;
153
event.channel = ev->data.note.channel;
155
event.is_noteon = true;
156
if( event.value == 0 )
157
event.is_noteon = false;
159
case SND_SEQ_EVENT_NOTEOFF:
160
event.type = tX_midievent::NOTE;
161
event.number = ev->data.note.note;
162
event.value = ev->data.note.velocity / 127.0;
163
event.channel = ev->data.note.channel;
165
event.is_noteon = false;
168
event_usable = false;
171
snd_seq_free_event(ev);
174
if (event.channel<0 || event.channel>15) {
175
tX_error("tX_midiin::check_event(): invaild event channel %i.", event.channel);
180
sp_to_learn->bound_midi_event=event;
184
gtk_widget_destroy(learn_dialog);
188
// This should be solved with a hash table. Possibly.
190
list <tX_seqpar *> :: iterator sp;
192
for (sp=tX_seqpar::all.begin(); sp!=tX_seqpar::all.end(); sp++) {
193
if ( (*sp)->bound_midi_event.type_matches (event) ) {
194
(*sp)->handle_midi_input (event);
206
void tX_midiin::configure_bindings( vtt_class* vtt )
208
list <tX_seqpar *> :: iterator sp;
210
GType types[3] = { G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER };
211
GtkListStore* model = gtk_list_store_newv(3, types);
215
for (sp=tX_seqpar::all.begin(); sp!=tX_seqpar::all.end(); sp++) {
216
if (((*sp)->is_mappable) && ((*sp)->vtt) == (void*) vtt) {
218
snprintf( tempstr, sizeof(tempstr), "Type: %d, Number: %d, Channel: %d",
219
(*sp)->bound_midi_event.type, (*sp)->bound_midi_event.number,
220
(*sp)->bound_midi_event.channel );
222
gtk_list_store_append( model, &iter );
223
gtk_list_store_set( model, &iter,
224
0, (*sp)->get_name(),
231
// it will delete itself.
232
new midi_binding_gui(GTK_TREE_MODEL(model), this);
235
tX_midiin::midi_binding_gui::midi_binding_gui ( GtkTreeModel* _model, tX_midiin* _midi )
236
: model(_model), midi( _midi )
239
GtkWidget *scrolledwindow1;
244
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
245
gtk_window_set_title (GTK_WINDOW (window), "Configure MIDI Bindings");
246
gtk_window_set_default_size(GTK_WINDOW(window), 600, 260);
248
hbox1 = gtk_hbox_new (FALSE, 2);
249
gtk_widget_show (hbox1);
250
gtk_container_add (GTK_CONTAINER (window), hbox1);
251
gtk_container_set_border_width(GTK_CONTAINER(hbox1), 4);
253
scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL);
254
gtk_widget_show (scrolledwindow1);
255
gtk_box_pack_start (GTK_BOX (hbox1), scrolledwindow1, TRUE, TRUE, 0);
257
parameter_treeview = gtk_tree_view_new_with_model (model);
258
gtk_widget_show (parameter_treeview);
259
gtk_container_add (GTK_CONTAINER (scrolledwindow1), parameter_treeview);
261
GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
262
gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( parameter_treeview ),
263
-1, "Parameter", renderer,
266
gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( parameter_treeview ),
267
-1, "Event", renderer,
270
gtk_tree_view_set_headers_visible( GTK_TREE_VIEW(parameter_treeview), TRUE );
272
vbox1 = gtk_vbox_new (FALSE, 0);
273
gtk_widget_show (vbox1);
274
gtk_box_pack_start (GTK_BOX (hbox1), vbox1, FALSE, FALSE, 0);
276
label1 = gtk_label_new ("Selected MIDI Event:");
277
gtk_widget_show (label1);
278
gtk_box_pack_start (GTK_BOX (vbox1), label1, FALSE, FALSE, 0);
279
gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
281
frame1 = gtk_frame_new (NULL);
282
gtk_widget_show (frame1);
283
gtk_box_pack_start (GTK_BOX (vbox1), frame1, TRUE, TRUE, 0);
284
gtk_container_set_border_width (GTK_CONTAINER (frame1), 2);
285
gtk_frame_set_label_align (GTK_FRAME (frame1), 0, 0);
286
gtk_frame_set_shadow_type (GTK_FRAME (frame1), GTK_SHADOW_IN);
288
midi_event_info = gtk_label_new ("Use a MIDI thing to select it.");
289
gtk_widget_show (midi_event_info);
290
gtk_container_add (GTK_CONTAINER (frame1), midi_event_info);
291
gtk_label_set_justify (GTK_LABEL (midi_event_info), GTK_JUSTIFY_LEFT);
293
bind_button = gtk_button_new_with_mnemonic ("Bind");
294
gtk_widget_show (bind_button);
295
gtk_box_pack_start (GTK_BOX (vbox1), bind_button, FALSE, FALSE, 0);
297
GtkWidget* unbind_button = gtk_button_new_with_mnemonic ("Remove Binding");
298
gtk_widget_show (unbind_button);
299
gtk_box_pack_start (GTK_BOX (vbox1), unbind_button, FALSE, FALSE, 0);
301
GtkWidget* close_button = gtk_button_new_with_mnemonic ("Close");
302
gtk_widget_show (close_button);
303
gtk_box_pack_start (GTK_BOX (vbox1), close_button, FALSE, FALSE, 0);
305
g_signal_connect(G_OBJECT(bind_button), "clicked", (GtkSignalFunc) bind_clicked, (void *) this);
306
g_signal_connect(G_OBJECT(unbind_button), "clicked", (GtkSignalFunc) unbind_clicked, (void *) this);
307
g_signal_connect(G_OBJECT(close_button), "clicked", (GtkSignalFunc) close_clicked, (void *) this);
308
g_signal_connect(G_OBJECT(window), "destroy", (GtkSignalFunc) close_clicked, (void *) this);
310
timer_tag = gtk_timeout_add( 100, (GtkFunction) timer, (void *) this);
312
gtk_widget_show_all( GTK_WIDGET( window ) );
315
void tX_midiin::midi_binding_gui::window_closed(GtkWidget *widget, gpointer _this )
317
tX_midiin::midi_binding_gui* this_ = (tX_midiin::midi_binding_gui*)_this;
322
void tX_midiin::midi_binding_gui::unbind_clicked( GtkButton *button, gpointer _this )
324
tX_midiin::midi_binding_gui* this_ = (tX_midiin::midi_binding_gui*)_this;
326
GtkTreeSelection* selection;
331
selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(this_->parameter_treeview) );
332
gtk_tree_selection_get_selected( selection, &model, &iter );
333
gtk_tree_model_get( model, &iter, 2, ¶m, -1 );
335
param->bound_midi_event.type=tX_midievent::NONE;
336
param->bound_midi_event.number=0;
337
param->bound_midi_event.channel=0;
339
snprintf( tmpstr, sizeof(tmpstr), "Type: %d, Number: %d, Channel: %d",
340
param->bound_midi_event.type, param->bound_midi_event.number,
341
param->bound_midi_event.channel );
343
gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, param->get_name(), 1, tmpstr, 2, param, -1 );
347
void tX_midiin::midi_binding_gui::bind_clicked( GtkButton *button, gpointer _this )
349
tX_midiin::midi_binding_gui* this_ = (tX_midiin::midi_binding_gui*)_this;
351
GtkTreeSelection* selection;
356
selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(this_->parameter_treeview) );
357
gtk_tree_selection_get_selected( selection, &model, &iter );
358
gtk_tree_model_get( model, &iter, 2, ¶m, -1 );
360
param->bound_midi_event = this_->last_event;
362
snprintf( tmpstr, sizeof(tmpstr), "Type: %d, Number: %d, Channel: %d",
363
param->bound_midi_event.type, param->bound_midi_event.number,
364
param->bound_midi_event.channel );
366
gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, param->get_name(), 1, tmpstr, 2, param, -1 );
369
void tX_midiin::midi_binding_gui::close_clicked( GtkButton *button, gpointer _this )
371
tX_midiin::midi_binding_gui* this_ = (tX_midiin::midi_binding_gui*)_this;
373
gtk_widget_destroy( this_->window );
376
gint tX_midiin::midi_binding_gui::timer( gpointer _this )
378
tX_midiin::midi_binding_gui* this_ = (tX_midiin::midi_binding_gui*)_this;
379
tX_midievent tmpevent = this_->midi->get_last_event();
381
if( tmpevent.type_matches( this_->last_event ) )
384
this_->last_event = tmpevent;
385
this_->last_event.clear_non_type();
387
snprintf( this_->tempstr, sizeof(this_->tempstr),
388
"Type: %d (CC=%d, NOTE=%d)\nNumber: %d\nChannel: %d\n",
389
this_->last_event.type, tX_midievent::CC, tX_midievent::NOTE,
390
this_->last_event.number,
391
this_->last_event.channel );
393
gtk_label_set_text( GTK_LABEL(this_->midi_event_info), this_->tempstr );
398
tX_midiin::midi_binding_gui::~midi_binding_gui ()
400
gtk_timeout_remove( timer_tag );
403
void tX_midiin::set_midi_learn_sp(tX_seqpar *sp)
408
gtk_widget_destroy(learn_dialog);
413
if (!sp_to_learn) return;
415
learn_dialog=create_tX_midilearn();
416
tX_set_icon(learn_dialog);
417
GtkWidget *label=lookup_widget(learn_dialog, "midilabel");
419
sprintf(buffer, "Learning MIDI mapping for <b>%s</b>\nfor turntable <b>%s</b>.\n\nWaiting for MIDI event...", sp->get_name(), sp->get_vtt_name());
420
gtk_label_set_markup(GTK_LABEL(label), buffer);
421
gtk_widget_show(learn_dialog);
423
g_signal_connect(G_OBJECT(lookup_widget(learn_dialog, "cancel")), "clicked", G_CALLBACK (tX_midiin::midi_learn_cancel), this);
424
g_signal_connect(G_OBJECT(learn_dialog), "destroy", G_CALLBACK (tX_midiin::midi_learn_destroy), this);
427
void tX_midiin::cancel_midi_learn()
433
gboolean tX_midiin::midi_learn_cancel(GtkWidget *widget, tX_midiin *midi)
435
midi->sp_to_learn=NULL;
436
gtk_widget_destroy(midi->learn_dialog);
441
gboolean tX_midiin::midi_learn_destroy(GtkWidget *widget, tX_midiin *midi)
443
midi->cancel_midi_learn();
448
void tX_midiin::store_connections(FILE *rc, char *indent)
452
tX_store("%s<midi_connections>\n", indent);
453
strcat(indent, "\t");
455
snd_seq_addr_t my_addr;
456
my_addr.client=snd_seq_client_id(ALSASeqHandle);
459
snd_seq_query_subscribe_t *subs;
460
snd_seq_query_subscribe_alloca(&subs);
461
snd_seq_query_subscribe_set_root(subs, &my_addr);
462
snd_seq_query_subscribe_set_type(subs, SND_SEQ_QUERY_SUBS_WRITE);
463
snd_seq_query_subscribe_set_index(subs, 0);
465
while (snd_seq_query_port_subscribers(ALSASeqHandle, subs) >= 0) {
466
const snd_seq_addr_t *addr;
467
addr = snd_seq_query_subscribe_get_addr(subs);
469
tX_store("%s<link client=\"%i\" port=\"%i\"/>\n", indent, addr->client, addr->port);
470
snd_seq_query_subscribe_set_index(subs, snd_seq_query_subscribe_get_index(subs) + 1);
473
indent[strlen(indent)-1]=0;
474
tX_store("%s</midi_connections>\n", indent);
477
void tX_midiin::restore_connections(xmlNodePtr node)
479
snd_seq_addr_t my_addr;
480
my_addr.client=snd_seq_client_id(ALSASeqHandle);
483
if (xmlStrcmp(node->name, (xmlChar *) "midi_connections")==0) {
484
for (xmlNodePtr cur=node->xmlChildrenNode; cur != NULL; cur = cur->next) {
485
if (cur->type == XML_ELEMENT_NODE) {
486
if (xmlStrcmp(cur->name, (xmlChar *) "link")==0) {
491
buffer=(char *) xmlGetProp(cur, (xmlChar *) "client");
493
sscanf(buffer, "%i", &client);
496
buffer=(char *) xmlGetProp(cur, (xmlChar *) "port");
498
sscanf(buffer, "%i", &port);
501
if ((port>=0) && (client>=0)) {
502
snd_seq_addr_t sender_addr;
503
sender_addr.client=client;
504
sender_addr.port=port;
506
snd_seq_port_subscribe_t *subs;
507
snd_seq_port_subscribe_alloca(&subs);
508
snd_seq_port_subscribe_set_sender(subs, &sender_addr);
509
snd_seq_port_subscribe_set_dest(subs, &my_addr);
511
if (snd_seq_subscribe_port(ALSASeqHandle, subs) < 0) {
512
tX_error("tX_midiin::restore_connections() -> failed to connect to: %d:%d.", port, client);
515
tX_error("tX_midiin::restore_connections() -> invalid port: %d:%d.", port, client);
519
tX_error("tX_midiin::restore_connections() -> invalid element: %s.", cur->name);
524
tX_error("tX_midiin::restore_connections() -> invalid XML element.");
529
void tX_midiin_store_connections(FILE *rc, char *indent);
530
void tX_midiin_restore_connections(xmlNodePtr node);
533
void tX_midiin_store_connections(FILE *rc, char *indent)
535
tX_engine::get_instance()->get_midi()->store_connections(rc, indent);
538
void tX_midiin_restore_connections(xmlNodePtr node)
540
tX_engine::get_instance()->get_midi()->restore_connections(node);
543
static inline void cc_map(tX_seqpar *sp, int channel, int number) {
544
if (sp->bound_midi_event.type==tX_midievent::NONE) {
545
sp->bound_midi_event.type=tX_midievent::CC;
546
sp->bound_midi_event.channel=channel;
547
sp->bound_midi_event.number=number;
548
sp->reset_upper_midi_bound();
549
sp->reset_lower_midi_bound();
553
static inline void cc_note(tX_seqpar *sp, int channel, int number) {
554
if (sp->bound_midi_event.type==tX_midievent::NONE) {
555
sp->bound_midi_event.type=tX_midievent::NOTE;
556
sp->bound_midi_event.channel=channel;
557
sp->bound_midi_event.number=number;
558
sp->reset_upper_midi_bound();
559
sp->reset_lower_midi_bound();
563
void tX_midiin::auto_assign_midi_mappings(GtkWidget *widget, gpointer dummy)
565
std::list<vtt_class *>::iterator vtt;
569
GtkWidget *dialog=gtk_message_dialog_new(GTK_WINDOW(main_window),
570
GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO,
571
"Assigning the default mappings will overwrite existing MIDI mappings. OK, to overwrite existing MIDI mappings?");
573
int res=gtk_dialog_run(GTK_DIALOG(dialog));
574
gtk_widget_destroy(dialog);
576
if (res!=GTK_RESPONSE_YES) {
581
/* Works on my hardware :) */
582
cc_map(&sp_master_volume, 0, 28);
583
cc_map(&sp_master_volume, 0, 29);
585
for (vtt=vtt_class::main_list.begin(); (vtt!=vtt_class::main_list.end()) && (ctr<16); vtt++, ctr++) {
586
/* These are pretty standard... */
587
cc_map((&(*vtt)->sp_volume), ctr, 07);
588
cc_map((&(*vtt)->sp_pan), ctr, 10);
589
cc_map((&(*vtt)->sp_lp_freq), ctr, 13);
590
cc_map((&(*vtt)->sp_lp_reso), ctr, 12);
592
/* These are on "general purpose"... */
593
cc_map((&(*vtt)->sp_lp_gain), ctr, 16);
594
cc_map((&(*vtt)->sp_speed), ctr, 17);
595
cc_map((&(*vtt)->sp_pitch), ctr, 18);
596
cc_map((&(*vtt)->sp_sync_cycles), ctr, 19);
598
/* Sound Controller 6-10 */
599
cc_map((&(*vtt)->sp_ec_length), ctr, 75);
600
cc_map((&(*vtt)->sp_ec_feedback), ctr, 76);
601
cc_map((&(*vtt)->sp_ec_volume), ctr, 77);
602
cc_map((&(*vtt)->sp_ec_pan), ctr, 78);
604
/* The toggles mapped to notes... */
605
cc_note((&(*vtt)->sp_trigger), 0, 60+ctr);
606
cc_note((&(*vtt)->sp_sync_client), 1, 60+ctr);
607
cc_note((&(*vtt)->sp_loop), 2, 60+ctr);
608
cc_note((&(*vtt)->sp_lp_enable), 3, 60+ctr);
609
cc_note((&(*vtt)->sp_ec_enable), 4, 60+ctr);
610
cc_note((&(*vtt)->sp_mute), 5, 60+ctr);
611
cc_note((&(*vtt)->sp_spin), 6, 60+ctr);
615
void tX_midiin::clear_midi_mappings(GtkWidget *widget, gpointer dummy)
617
std::list<tX_seqpar *>::iterator sp;
620
GtkWidget *dialog=gtk_message_dialog_new(GTK_WINDOW(main_window),
621
GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO,
622
"Really clear all current MIDI mappings?");
624
int res=gtk_dialog_run(GTK_DIALOG(dialog));
625
gtk_widget_destroy(dialog);
627
if (res!=GTK_RESPONSE_YES) {
632
for (sp=tX_seqpar::all.begin(); sp!=tX_seqpar::all.end(); sp++) {
633
(*sp)->bound_midi_event.type=tX_midievent::NONE;
634
(*sp)->bound_midi_event.channel=0;
635
(*sp)->bound_midi_event.number=0;
636
(*sp)->reset_upper_midi_bound();
637
(*sp)->reset_lower_midi_bound();
640
#endif // USE_ALSA_MIDI_IN