Mostly MIDI fixes - Alex
[terminatorX.git] / terminatorX / 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
24 #include "tX_midiin.h"
25 #include "tX_vtt.h"
26
27 #ifdef USE_ALSA_MIDI_IN
28 #include "tX_global.h"
29 #include <iostream>
30
31 using namespace std;
32
33 static gboolean midi_callback(GIOChannel *source, GIOCondition condition, gpointer data) {
34         tX_midiin *midi=(tX_midiin *) data;
35         midi->check_event();
36 }
37
38 tX_midiin::tX_midiin()
39 {
40         
41         int portid;
42         is_open=false;
43         
44         if (snd_seq_open(&ALSASeqHandle, "default", SND_SEQ_OPEN_INPUT, 0) < 0) {
45                 tX_error("tX_midiin(): failed to open the default sequencer device.");
46                 return;
47         }
48         snd_seq_set_client_name(ALSASeqHandle, "terminatorX");
49         portid =
50                 snd_seq_create_simple_port(ALSASeqHandle,
51                                                                    "Control Input",
52                                                                    SND_SEQ_PORT_CAP_WRITE
53                                                                    | SND_SEQ_PORT_CAP_SUBS_WRITE,
54                                                                    SND_SEQ_PORT_TYPE_APPLICATION);
55         if (portid < 0) {
56                 tX_error("tX_midiin(): error creating sequencer port.");
57                 return;
58         }
59
60         snd_seq_nonblock( ALSASeqHandle, 1 );
61         
62         struct pollfd fds[32];
63         
64         int res=snd_seq_poll_descriptors (ALSASeqHandle, fds, 32, POLLIN);
65
66         if (res!=1) {
67                 tX_error("Failed to poll ALSA descriptors: %i.\n", res);
68         }
69         
70         GIOChannel *ioc=g_io_channel_unix_new(fds[0].fd);
71         g_io_add_watch(ioc, (GIOCondition)( G_IO_IN ), midi_callback, (gpointer) this);
72         g_io_channel_unref(ioc);
73         
74         is_open=true;
75
76         tX_debug("tX_midiin(): sequencer successfully opened."); 
77 }
78
79 tX_midiin::~tX_midiin()
80 {
81         snd_seq_close(ALSASeqHandle);
82         tX_debug("tX_midiin(): sequencer closed."); 
83 }
84
85 int tX_midiin::check_event()
86 {
87         snd_seq_event_t *ev;
88                 
89         while( snd_seq_event_input(ALSASeqHandle, &ev) != -EAGAIN )
90         {
91
92                 //MidiEvent::type MessageType=MidiEvent::NONE;
93                 //int Volume=0,Note=0,EventDevice=0;
94                 tX_midievent event;
95                 event.is_noteon = false;
96                 bool event_usable = true;
97                 
98                 switch (ev->type) {
99                         case SND_SEQ_EVENT_CONTROLLER: 
100                                 event.type = tX_midievent::CC;
101                                 event.number = ev->data.control.param;
102                                 event.value = ev->data.control.value / 127.0;
103                                 event.channel = ev->data.control.channel;
104                                 break;
105                         case SND_SEQ_EVENT_PITCHBEND:
106                                 event.type = tX_midievent::PITCHBEND;
107                                 event.number = ev->data.control.param;
108                                 event.value = ev->data.control.value / 127.0;
109                                 event.channel = ev->data.control.channel;
110                                 break;
111                         case SND_SEQ_EVENT_NOTEON:
112                                 event.type = tX_midievent::NOTE;
113                                 event.number = ev->data.note.note;
114                                 event.value = ev->data.note.velocity / 127.0;
115                                 event.channel = ev->data.note.channel;
116
117                                 event.is_noteon = true;
118                                 if( event.value == 0 )
119                                         event.is_noteon = false;
120                                 break;
121                         case SND_SEQ_EVENT_NOTEOFF: 
122                                 event.type = tX_midievent::NOTE;
123                                 event.number = ev->data.note.note;
124                                 event.value = ev->data.note.velocity / 127.0;
125                                 event.channel = ev->data.note.channel;
126                                 
127                                 event.is_noteon = false;
128                                 break;
129                         default:
130                                 event_usable = false;
131                 }
132
133                 snd_seq_free_event(ev);
134                 
135                 if( event_usable )
136                 {
137                         if (event.channel<0 || event.channel>15)
138                         {
139                                 tX_error("tX_midiin::check_event(): invaild event channel %i.", event.channel);
140                                 return -1;
141                         }
142
143                         // This should be solved with a hash table. Possibly.
144                         
145                         list <tX_seqpar *> :: iterator sp;                      
146                         
147                         for (sp=tX_seqpar::all.begin(); sp!=tX_seqpar::all.end(); sp++) {
148                                 if ( (*sp)->bound_midi_event.type_matches (event) ) {
149                                         (*sp)->handle_midi_input (event);
150                                 }
151                         }
152
153                         last_event = event;
154                 }
155
156         }
157         return 1;
158 }
159
160 void tX_midiin::configure_bindings( vtt_class* vtt )
161 {
162         list <tX_seqpar *> :: iterator sp;
163
164         GType types[3] = { G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER };
165         GtkListStore* model = gtk_list_store_newv(3, types);
166         GtkTreeIter iter;
167         char tempstr[128];
168         
169         for (sp=tX_seqpar::all.begin(); sp!=tX_seqpar::all.end(); sp++) {
170                 if (((*sp)->is_mappable) && ((*sp)->vtt) == (void*) vtt) {
171                         
172                         snprintf( tempstr, sizeof(tempstr), "Type: %d, Number: %d, Channel: %d",
173                                           (*sp)->bound_midi_event.type, (*sp)->bound_midi_event.number,
174                                           (*sp)->bound_midi_event.channel );
175
176                         gtk_list_store_append( model, &iter );
177                         gtk_list_store_set( model, &iter,
178                                                                 0, (*sp)->get_name(),
179                                                                 1, tempstr,
180                                                                 2, (*sp),
181                                                                 -1 );
182                 }
183         }
184
185         // it will delete itself.
186         new midi_binding_gui(GTK_TREE_MODEL(model), this);
187 }
188
189 tX_midiin::midi_binding_gui::midi_binding_gui ( GtkTreeModel* _model, tX_midiin* _midi )
190         : model(_model), midi( _midi )
191 {
192         GtkWidget *hbox1;
193         GtkWidget *scrolledwindow1;
194         GtkWidget *vbox1;
195         GtkWidget *label1;
196         GtkWidget *frame1;
197         
198         window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
199         gtk_window_set_title (GTK_WINDOW (window), "Configure MIDI Bindings");
200         gtk_window_set_default_size(GTK_WINDOW(window), 600, 260);
201         
202         hbox1 = gtk_hbox_new (FALSE, 2);
203         gtk_widget_show (hbox1);
204         gtk_container_add (GTK_CONTAINER (window), hbox1);
205         gtk_container_set_border_width(GTK_CONTAINER(hbox1), 4);
206         
207         scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL);
208         gtk_widget_show (scrolledwindow1);
209         gtk_box_pack_start (GTK_BOX (hbox1), scrolledwindow1, TRUE, TRUE, 0);
210         
211         parameter_treeview = gtk_tree_view_new_with_model (model);
212         gtk_widget_show (parameter_treeview);
213         gtk_container_add (GTK_CONTAINER (scrolledwindow1), parameter_treeview);
214         
215         GtkCellRenderer   *renderer = gtk_cell_renderer_text_new ();
216         gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( parameter_treeview ),
217                                                                                            -1, "Parameter", renderer,
218                                                                                            "text", 0,
219                                                                                            NULL );
220         gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( parameter_treeview ),
221                                                                                            -1, "Event", renderer,
222                                                                                            "text", 1,
223                                                                                            NULL );
224         gtk_tree_view_set_headers_visible( GTK_TREE_VIEW(parameter_treeview), TRUE );
225         
226         vbox1 = gtk_vbox_new (FALSE, 0);
227         gtk_widget_show (vbox1);
228         gtk_box_pack_start (GTK_BOX (hbox1), vbox1, FALSE, FALSE, 0);
229         
230         label1 = gtk_label_new ("Selected MIDI Event:");
231         gtk_widget_show (label1);
232         gtk_box_pack_start (GTK_BOX (vbox1), label1, FALSE, FALSE, 0);
233         gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
234         
235         frame1 = gtk_frame_new (NULL);
236         gtk_widget_show (frame1);
237         gtk_box_pack_start (GTK_BOX (vbox1), frame1, TRUE, TRUE, 0);
238         gtk_container_set_border_width (GTK_CONTAINER (frame1), 2);
239         gtk_frame_set_label_align (GTK_FRAME (frame1), 0, 0);
240         gtk_frame_set_shadow_type (GTK_FRAME (frame1), GTK_SHADOW_IN);
241         
242         midi_event_info = gtk_label_new ("Use a MIDI thing to select it.");
243         gtk_widget_show (midi_event_info);
244         gtk_container_add (GTK_CONTAINER (frame1), midi_event_info);
245         gtk_label_set_justify (GTK_LABEL (midi_event_info), GTK_JUSTIFY_LEFT);
246         
247         bind_button = gtk_button_new_with_mnemonic ("Bind");
248         gtk_widget_show (bind_button);
249         gtk_box_pack_start (GTK_BOX (vbox1), bind_button, FALSE, FALSE, 0);
250         
251         GtkWidget* unbind_button = gtk_button_new_with_mnemonic ("Remove Binding");
252         gtk_widget_show (unbind_button);
253         gtk_box_pack_start (GTK_BOX (vbox1), unbind_button, FALSE, FALSE, 0);   
254         
255         GtkWidget* close_button = gtk_button_new_with_mnemonic ("Close");
256         gtk_widget_show (close_button);
257         gtk_box_pack_start (GTK_BOX (vbox1), close_button, FALSE, FALSE, 0);
258         
259         gtk_signal_connect(GTK_OBJECT(bind_button), "clicked", (GtkSignalFunc) bind_clicked, (void *) this);
260         gtk_signal_connect(GTK_OBJECT(unbind_button), "clicked", (GtkSignalFunc) unbind_clicked, (void *) this);        
261         gtk_signal_connect(GTK_OBJECT(close_button), "clicked", (GtkSignalFunc) close_clicked, (void *) this);
262         gtk_signal_connect(GTK_OBJECT(window), "destroy", (GtkSignalFunc) close_clicked, (void *) this);
263         
264         timer_tag = gtk_timeout_add( 100, (GtkFunction) timer, (void *) this);
265         
266         gtk_widget_show_all( GTK_WIDGET( window ) );
267 }
268
269 void tX_midiin::midi_binding_gui::window_closed(GtkWidget *widget, gpointer _this )
270 {
271         tX_midiin::midi_binding_gui* this_ = (tX_midiin::midi_binding_gui*)_this;
272
273         delete this_;
274 }
275
276 void tX_midiin::midi_binding_gui::unbind_clicked( GtkButton *button, gpointer _this )
277 {
278         tX_midiin::midi_binding_gui* this_ = (tX_midiin::midi_binding_gui*)_this;
279         GtkTreeModel* model;
280         GtkTreeSelection* selection;
281         GtkTreeIter iter;
282         char tmpstr[128];
283         tX_seqpar* param;
284
285         selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(this_->parameter_treeview) );
286         gtk_tree_selection_get_selected( selection, &model, &iter );
287         gtk_tree_model_get( model, &iter, 2, &param, -1 );
288         
289         param->bound_midi_event.type=tX_midievent::NONE;
290         param->bound_midi_event.number=0;
291         param->bound_midi_event.channel=0;
292         
293         snprintf( tmpstr, sizeof(tmpstr), "Type: %d, Number: %d, Channel: %d",
294                                 param->bound_midi_event.type, param->bound_midi_event.number,
295                                 param->bound_midi_event.channel );
296
297         gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, param->get_name(), 1, tmpstr, 2, param, -1 );       
298 }
299
300
301 void tX_midiin::midi_binding_gui::bind_clicked( GtkButton *button, gpointer _this )
302 {
303         tX_midiin::midi_binding_gui* this_ = (tX_midiin::midi_binding_gui*)_this;
304         GtkTreeModel* model;
305         GtkTreeSelection* selection;
306         GtkTreeIter iter;
307         char tmpstr[128];
308         tX_seqpar* param;
309
310         selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(this_->parameter_treeview) );
311         gtk_tree_selection_get_selected( selection, &model, &iter );
312         gtk_tree_model_get( model, &iter, 2, &param, -1 );
313         
314         param->bound_midi_event = this_->last_event;
315         
316         snprintf( tmpstr, sizeof(tmpstr), "Type: %d, Number: %d, Channel: %d",
317                                 param->bound_midi_event.type, param->bound_midi_event.number,
318                                 param->bound_midi_event.channel );
319
320         gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, param->get_name(), 1, tmpstr, 2, param, -1 );       
321 }
322
323 void tX_midiin::midi_binding_gui::close_clicked( GtkButton *button, gpointer _this )
324 {
325         tX_midiin::midi_binding_gui* this_ = (tX_midiin::midi_binding_gui*)_this;
326         
327         gtk_widget_destroy( this_->window );
328
329         delete this_;
330 }
331
332 gint tX_midiin::midi_binding_gui::timer( gpointer _this )
333 {
334         tX_midiin::midi_binding_gui* this_ = (tX_midiin::midi_binding_gui*)_this;
335         tX_midievent tmpevent = this_->midi->get_last_event();
336
337         if( tmpevent.type_matches( this_->last_event ) )
338                 return TRUE;
339         
340         this_->last_event = tmpevent;
341         this_->last_event.clear_non_type();
342
343         snprintf( this_->tempstr, sizeof(this_->tempstr),
344                           "Type: %d (CC=%d, NOTE=%d)\nNumber: %d\nChannel: %d\n",
345                           this_->last_event.type, tX_midievent::CC, tX_midievent::NOTE,
346                           this_->last_event.number,
347                           this_->last_event.channel );
348
349         gtk_label_set_text( GTK_LABEL(this_->midi_event_info), this_->tempstr );
350
351         return TRUE;
352 }
353
354 tX_midiin::midi_binding_gui::~midi_binding_gui ()
355 {
356         gtk_timeout_remove( timer_tag );
357 }
358
359 #endif // USE_ALSA_MIDI_IN