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