Last minute stuff - Alex
[terminatorX.git] / src / tX_midiin.cc
1 /*
2   terminatorX - realtime audio scratching software
3   Copyright (C) 2002 Arthur Peters
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
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  
19   File: tX_midiin.cc
20  
21   Description: Implements MIDI input to control turntable parameters.
22   
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
30         for 3.81
31         - re-connect to MIDI devices
32         - auto MIDI mappings
33 */    
34
35 #include "tX_midiin.h"
36 #include "tX_vtt.h"
37 #include "tX_glade_interface.h"
38 #include "tX_glade_support.h"
39 #include "tX_dialog.h"
40 #include "tX_mastergui.h"
41
42 #ifdef USE_ALSA_MIDI_IN
43 #include "tX_global.h"
44 #include <iostream>
45 #include "tX_engine.h"
46
47 using namespace std;
48
49 static gboolean midi_callback(GIOChannel *source, GIOCondition condition, gpointer data) {
50         tX_midiin *midi=(tX_midiin *) data;
51         midi->check_event();
52         
53         return TRUE;
54 }
55
56 tX_midiin::tX_midiin()
57 {
58         is_open=false;
59         sp_to_learn=NULL;
60         learn_dialog=NULL;
61         
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.");
64                 return;
65         }
66         snd_seq_set_client_name(ALSASeqHandle, "terminatorX");
67         portid =
68                 snd_seq_create_simple_port(ALSASeqHandle,
69                                                                    "Control Input",
70                                                                    SND_SEQ_PORT_CAP_WRITE
71                                                                    | SND_SEQ_PORT_CAP_SUBS_WRITE,
72                                                                    SND_SEQ_PORT_TYPE_APPLICATION);
73         if (portid < 0) {
74                 tX_error("tX_midiin(): error creating sequencer port.");
75                 return;
76         }
77
78         snd_seq_nonblock( ALSASeqHandle, 1 );
79         
80         struct pollfd fds[32];
81         
82         int res=snd_seq_poll_descriptors (ALSASeqHandle, fds, 32, POLLIN);
83
84         if (res!=1) {
85                 tX_error("Failed to poll ALSA descriptors: %i.\n", res);
86         }
87         
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);
91         
92         is_open=true;
93
94         tX_debug("tX_midiin(): sequencer successfully opened."); 
95 }
96
97 tX_midiin::~tX_midiin()
98 {
99         if (is_open) {
100                 snd_seq_close(ALSASeqHandle);
101                 tX_debug("tX_midiin(): sequencer closed.");
102         }
103 }
104
105 int tX_midiin::check_event()
106 {
107         snd_seq_event_t *ev;
108                 
109         while( snd_seq_event_input(ALSASeqHandle, &ev) != -EAGAIN )
110         {
111
112                 //MidiEvent::type MessageType=MidiEvent::NONE;
113                 //int Volume=0,Note=0,EventDevice=0;
114                 tX_midievent event;
115                 event.is_noteon = false;
116                 bool event_usable = true;
117
118                 switch (ev->type) {
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;
124                                 break;
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;
130                                 break;
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;
136                                 break;
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;
142                                 break;
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;
148                                 break;                                          
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;
154
155                                 event.is_noteon = true;
156                                 if( event.value == 0 )
157                                         event.is_noteon = false;
158                                 break;
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;
164                                 
165                                 event.is_noteon = false;
166                                 break;
167                         default:
168                                 event_usable = false;
169                 }
170
171                 snd_seq_free_event(ev);
172                 
173                 if( event_usable ) {
174                         if (event.channel<0 || event.channel>15) {
175                                 tX_error("tX_midiin::check_event(): invaild event channel %i.", event.channel);
176                                 return -1;
177                         }
178
179                         if (sp_to_learn) {
180                                 sp_to_learn->bound_midi_event=event;
181                                 sp_to_learn=NULL;
182                                 
183                                 if (learn_dialog) {
184                                         gtk_widget_destroy(learn_dialog);
185                                         learn_dialog=NULL;
186                                 }
187                         } else {
188                                 // This should be solved with a hash table. Possibly.
189                                 
190                                 list <tX_seqpar *> :: iterator sp;                      
191                                 
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);
195                                         }
196                                 }
197                         }
198                         
199                         last_event = event;
200                 }
201
202         }
203         return 1;
204 }
205
206 void tX_midiin::configure_bindings( vtt_class* vtt )
207 {
208         list <tX_seqpar *> :: iterator sp;
209
210         GType types[3] = { G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER };
211         GtkListStore* model = gtk_list_store_newv(3, types);
212         GtkTreeIter iter;
213         char tempstr[128];
214         
215         for (sp=tX_seqpar::all.begin(); sp!=tX_seqpar::all.end(); sp++) {
216                 if (((*sp)->is_mappable) && ((*sp)->vtt) == (void*) vtt) {
217                         
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 );
221
222                         gtk_list_store_append( model, &iter );
223                         gtk_list_store_set( model, &iter,
224                                                                 0, (*sp)->get_name(),
225                                                                 1, tempstr,
226                                                                 2, (*sp),
227                                                                 -1 );
228                 }
229         }
230
231         // it will delete itself.
232         new midi_binding_gui(GTK_TREE_MODEL(model), this);
233 }
234
235 tX_midiin::midi_binding_gui::midi_binding_gui ( GtkTreeModel* _model, tX_midiin* _midi )
236         : model(_model), midi( _midi )
237 {
238         GtkWidget *hbox1;
239         GtkWidget *scrolledwindow1;
240         GtkWidget *vbox1;
241         GtkWidget *label1;
242         GtkWidget *frame1;
243         
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);
247         
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);
252         
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);
256         
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);
260         
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,
264                                                                                            "text", 0,
265                                                                                            NULL );
266         gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( parameter_treeview ),
267                                                                                            -1, "Event", renderer,
268                                                                                            "text", 1,
269                                                                                            NULL );
270         gtk_tree_view_set_headers_visible( GTK_TREE_VIEW(parameter_treeview), TRUE );
271         
272         vbox1 = gtk_vbox_new (FALSE, 0);
273         gtk_widget_show (vbox1);
274         gtk_box_pack_start (GTK_BOX (hbox1), vbox1, FALSE, FALSE, 0);
275         
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);
280         
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);
287         
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);
292         
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);
296         
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);   
300         
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);
304         
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);
309         
310         timer_tag = gtk_timeout_add( 100, (GtkFunction) timer, (void *) this);
311         
312         gtk_widget_show_all( GTK_WIDGET( window ) );
313 }
314
315 void tX_midiin::midi_binding_gui::window_closed(GtkWidget *widget, gpointer _this )
316 {
317         tX_midiin::midi_binding_gui* this_ = (tX_midiin::midi_binding_gui*)_this;
318
319         delete this_;
320 }
321
322 void tX_midiin::midi_binding_gui::unbind_clicked( GtkButton *button, gpointer _this )
323 {
324         tX_midiin::midi_binding_gui* this_ = (tX_midiin::midi_binding_gui*)_this;
325         GtkTreeModel* model;
326         GtkTreeSelection* selection;
327         GtkTreeIter iter;
328         char tmpstr[128];
329         tX_seqpar* param;
330
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, &param, -1 );
334         
335         param->bound_midi_event.type=tX_midievent::NONE;
336         param->bound_midi_event.number=0;
337         param->bound_midi_event.channel=0;
338         
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 );
342
343         gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, param->get_name(), 1, tmpstr, 2, param, -1 );       
344 }
345
346
347 void tX_midiin::midi_binding_gui::bind_clicked( GtkButton *button, gpointer _this )
348 {
349         tX_midiin::midi_binding_gui* this_ = (tX_midiin::midi_binding_gui*)_this;
350         GtkTreeModel* model;
351         GtkTreeSelection* selection;
352         GtkTreeIter iter;
353         char tmpstr[128];
354         tX_seqpar* param;
355
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, &param, -1 );
359         
360         param->bound_midi_event = this_->last_event;
361         
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 );
365
366         gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, param->get_name(), 1, tmpstr, 2, param, -1 );       
367 }
368
369 void tX_midiin::midi_binding_gui::close_clicked( GtkButton *button, gpointer _this )
370 {
371         tX_midiin::midi_binding_gui* this_ = (tX_midiin::midi_binding_gui*)_this;
372         
373         gtk_widget_destroy( this_->window );
374 }
375
376 gint tX_midiin::midi_binding_gui::timer( gpointer _this )
377 {
378         tX_midiin::midi_binding_gui* this_ = (tX_midiin::midi_binding_gui*)_this;
379         tX_midievent tmpevent = this_->midi->get_last_event();
380
381         if( tmpevent.type_matches( this_->last_event ) )
382                 return TRUE;
383         
384         this_->last_event = tmpevent;
385         this_->last_event.clear_non_type();
386
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 );
392
393         gtk_label_set_text( GTK_LABEL(this_->midi_event_info), this_->tempstr );
394
395         return TRUE;
396 }
397
398 tX_midiin::midi_binding_gui::~midi_binding_gui ()
399 {
400         gtk_timeout_remove( timer_tag );
401 }
402
403 void tX_midiin::set_midi_learn_sp(tX_seqpar *sp)
404 {
405         char buffer[512];
406         
407         if (learn_dialog) {
408                 gtk_widget_destroy(learn_dialog);
409         }
410         
411         sp_to_learn=sp;
412         
413         if (!sp_to_learn) return;
414         
415         learn_dialog=create_tX_midilearn();
416         tX_set_icon(learn_dialog);
417         GtkWidget *label=lookup_widget(learn_dialog, "midilabel");
418         
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);
422         
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);
425 }
426
427 void tX_midiin::cancel_midi_learn()
428 {
429         sp_to_learn=NULL;
430         learn_dialog=NULL;
431 }
432
433 gboolean tX_midiin::midi_learn_cancel(GtkWidget *widget, tX_midiin *midi)
434 {
435         midi->sp_to_learn=NULL;
436         gtk_widget_destroy(midi->learn_dialog);
437         
438         return FALSE;
439 }
440
441 gboolean tX_midiin::midi_learn_destroy(GtkWidget *widget, tX_midiin *midi)
442 {
443         midi->cancel_midi_learn();
444         
445         return FALSE;
446 }
447
448 void tX_midiin::store_connections(FILE *rc, char *indent) 
449 {
450         gzFile *rz=NULL;
451         
452         tX_store("%s<midi_connections>\n", indent);
453         strcat(indent, "\t");
454
455         snd_seq_addr_t my_addr;
456         my_addr.client=snd_seq_client_id(ALSASeqHandle);
457         my_addr.port=portid;
458
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);
464         
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);
468                 
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);
471         }       
472                 
473         indent[strlen(indent)-1]=0;
474         tX_store("%s</midi_connections>\n", indent);    
475 }
476
477 void tX_midiin::restore_connections(xmlNodePtr node)
478 {
479         snd_seq_addr_t my_addr;
480         my_addr.client=snd_seq_client_id(ALSASeqHandle);
481         my_addr.port=portid;
482         
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) {
487                                         char *buffer;
488                                         int client=-1;
489                                         int port=-1;
490                                         
491                                         buffer=(char *) xmlGetProp(cur, (xmlChar *) "client");
492                                         if (buffer) {
493                                                 sscanf(buffer, "%i", &client);
494                                         }
495                                         
496                                         buffer=(char *) xmlGetProp(cur, (xmlChar *) "port");
497                                         if (buffer) {
498                                                 sscanf(buffer, "%i", &port);
499                                         }
500                                         
501                                         if ((port>=0) && (client>=0)) {
502                                                 snd_seq_addr_t sender_addr;
503                                                 sender_addr.client=client;
504                                                 sender_addr.port=port;
505                                                 
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);
510
511                                                 if (snd_seq_subscribe_port(ALSASeqHandle, subs) < 0) {
512                                                         tX_error("tX_midiin::restore_connections() -> failed to connect to: %d:%d.", port, client);
513                                                 }
514                                         } else {
515                                                 tX_error("tX_midiin::restore_connections() -> invalid port: %d:%d.", port, client);
516                                         }
517                                         
518                                 } else {
519                                         tX_error("tX_midiin::restore_connections() -> invalid element: %s.", cur->name);
520                                 }
521                         }
522                 }
523         } else {
524                 tX_error("tX_midiin::restore_connections() -> invalid XML element.");
525         }
526 }
527
528 extern "C" {
529         void tX_midiin_store_connections(FILE *rc, char *indent);
530         void tX_midiin_restore_connections(xmlNodePtr node);
531 };
532
533 void tX_midiin_store_connections(FILE *rc, char *indent) 
534 {
535         tX_engine::get_instance()->get_midi()->store_connections(rc, indent);
536 }
537
538 void tX_midiin_restore_connections(xmlNodePtr node) 
539 {
540         tX_engine::get_instance()->get_midi()->restore_connections(node);
541 }
542
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();
550         }
551 }
552
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();
560         }
561 }
562
563 void tX_midiin::auto_assign_midi_mappings(GtkWidget *widget, gpointer dummy)
564 {
565         std::list<vtt_class *>::iterator vtt;
566         int ctr=0;
567         
568 /*      if (dummy) {
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?");
572                 
573                 int res=gtk_dialog_run(GTK_DIALOG(dialog));
574                 gtk_widget_destroy(dialog);
575                         
576                 if (res!=GTK_RESPONSE_YES) {
577                         return;
578                 }               
579         } */
580         
581         /* Works on my hardware :) */
582         cc_map(&sp_master_volume, 0, 28);
583         cc_map(&sp_master_volume, 0, 29);       
584         
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);
591                 
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);
597                 
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);
603                 
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);
612         }
613 }
614
615 void tX_midiin::clear_midi_mappings(GtkWidget *widget, gpointer dummy)
616 {
617         std::list<tX_seqpar *>::iterator sp;
618         
619         if (dummy) {
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?");
623                 
624                 int res=gtk_dialog_run(GTK_DIALOG(dialog));
625                 gtk_widget_destroy(dialog);
626                         
627                 if (res!=GTK_RESPONSE_YES) {
628                         return;
629                 }               
630         }
631         
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();
638         }
639 }
640 #endif // USE_ALSA_MIDI_IN