Fixing a memory corruption - and changing the way tX_midiin works. Not
[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                         //cerr << event.type << ", " << event.number << ", " << event.value << endl;
144                         //event.print( __FUNCTION__ );
145
146                         list <tX_seqpar *> :: iterator sp;
147
148                         for (sp=tX_seqpar::all.begin(); sp!=tX_seqpar::all.end(); sp++) {
149                                 if ( (*sp)->bound_midi_event.type_matches (event) ) {
150                                         (*sp)->handle_midi_input (event);
151                                 }
152                         }
153
154                         last_event = event;
155                 }
156
157         }
158         return 1;
159 }
160
161 void tX_midiin::configure_bindings( vtt_class* vtt )
162 {
163         list <tX_seqpar *> :: iterator sp;
164
165         /*
166         tX_midievent event = {0,tX_midievent::CC,0,0,0};
167         event.type = tX_midievent::CC;
168         event.number = 11;
169         event.channel = 0;
170         */
171
172         GType types[3] = { G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER };
173         GtkListStore* model = gtk_list_store_newv(3, types);
174         GtkTreeIter iter;
175         char tempstr[128];
176         
177         for (sp=tX_seqpar::all.begin(); sp!=tX_seqpar::all.end(); sp++) {
178                 if (((*sp)->is_mappable) && ((*sp)->vtt) == (void*) vtt) {
179                         
180                         snprintf( tempstr, sizeof(tempstr), "Type: %d, Number: %d, Channel: %d",
181                                           (*sp)->bound_midi_event.type, (*sp)->bound_midi_event.number,
182                                           (*sp)->bound_midi_event.channel );
183
184                         gtk_list_store_append( model, &iter );
185                         gtk_list_store_set( model, &iter,
186                                                                 0, (*sp)->get_name(),
187                                                                 1, tempstr,
188                                                                 2, (*sp),
189                                                                 -1 );
190
191                         //cerr << (*sp)->get_name() << endl;
192                 }
193         }
194
195         // it will delete itself.
196         new midi_binding_gui(GTK_TREE_MODEL(model), this);
197
198         //cerr << "window created." << endl;
199
200         return;
201 }
202
203 tX_midiin::midi_binding_gui::midi_binding_gui ( GtkTreeModel* _model, tX_midiin* _midi )
204         : model(_model), midi( _midi )
205 {
206         GtkWidget *hbox1;
207         GtkWidget *scrolledwindow1;
208         GtkWidget *vbox1;
209         GtkWidget *label1;
210         GtkWidget *frame1;
211         
212         window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
213         gtk_window_set_title (GTK_WINDOW (window), "Configure MIDI Bindings");
214         gtk_window_set_default_size(GTK_WINDOW(window), 600, 260);
215         
216         hbox1 = gtk_hbox_new (FALSE, 2);
217         gtk_widget_show (hbox1);
218         gtk_container_add (GTK_CONTAINER (window), hbox1);
219         gtk_container_set_border_width(GTK_CONTAINER(hbox1), 4);
220         
221         scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL);
222         gtk_widget_show (scrolledwindow1);
223         gtk_box_pack_start (GTK_BOX (hbox1), scrolledwindow1, TRUE, TRUE, 0);
224         
225         parameter_treeview = gtk_tree_view_new_with_model (model);
226         gtk_widget_show (parameter_treeview);
227         gtk_container_add (GTK_CONTAINER (scrolledwindow1), parameter_treeview);
228         
229         GtkCellRenderer   *renderer = gtk_cell_renderer_text_new ();
230         gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( parameter_treeview ),
231                                                                                            -1, "Parameter", renderer,
232                                                                                            "text", 0,
233                                                                                            NULL );
234         gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW( parameter_treeview ),
235                                                                                            -1, "Event", renderer,
236                                                                                            "text", 1,
237                                                                                            NULL );
238         gtk_tree_view_set_headers_visible( GTK_TREE_VIEW(parameter_treeview), TRUE );
239         
240         vbox1 = gtk_vbox_new (FALSE, 0);
241         gtk_widget_show (vbox1);
242         gtk_box_pack_start (GTK_BOX (hbox1), vbox1, FALSE, FALSE, 0);
243         
244         label1 = gtk_label_new ("Selected MIDI Event:");
245         gtk_widget_show (label1);
246         gtk_box_pack_start (GTK_BOX (vbox1), label1, FALSE, FALSE, 0);
247         gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
248         
249         frame1 = gtk_frame_new (NULL);
250         gtk_widget_show (frame1);
251         gtk_box_pack_start (GTK_BOX (vbox1), frame1, TRUE, TRUE, 0);
252         gtk_container_set_border_width (GTK_CONTAINER (frame1), 2);
253         gtk_frame_set_label_align (GTK_FRAME (frame1), 0, 0);
254         gtk_frame_set_shadow_type (GTK_FRAME (frame1), GTK_SHADOW_IN);
255         
256         midi_event_info = gtk_label_new ("Use a MIDI thing to select it.");
257         gtk_widget_show (midi_event_info);
258         gtk_container_add (GTK_CONTAINER (frame1), midi_event_info);
259         gtk_label_set_justify (GTK_LABEL (midi_event_info), GTK_JUSTIFY_LEFT);
260         
261         bind_button = gtk_button_new_with_mnemonic ("Bind");
262         gtk_widget_show (bind_button);
263         gtk_box_pack_start (GTK_BOX (vbox1), bind_button, FALSE, FALSE, 0);
264         
265         GtkWidget* close_button = gtk_button_new_with_mnemonic ("Close");
266         gtk_widget_show (close_button);
267         gtk_box_pack_start (GTK_BOX (vbox1), close_button, FALSE, FALSE, 0);
268         
269         gtk_signal_connect(GTK_OBJECT(bind_button), "clicked", (GtkSignalFunc) bind_clicked, (void *) this);
270         gtk_signal_connect(GTK_OBJECT(close_button), "clicked", (GtkSignalFunc) close_clicked, (void *) this);
271         
272         timer_tag = gtk_timeout_add( 100, (GtkFunction) timer, (void *) this);
273         
274         gtk_widget_show_all( GTK_WIDGET( window ) );
275 }
276
277 void tX_midiin::midi_binding_gui::bind_clicked( GtkButton *button, gpointer _this )
278 {
279         tX_midiin::midi_binding_gui* this_ = (tX_midiin::midi_binding_gui*)_this;
280         GtkTreeModel* model;
281         GtkTreeSelection* selection;
282         GtkTreeIter iter;
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 = this_->last_event;
290 }
291
292 void tX_midiin::midi_binding_gui::close_clicked( GtkButton *button, gpointer _this )
293 {
294         tX_midiin::midi_binding_gui* this_ = (tX_midiin::midi_binding_gui*)_this;
295         
296         gtk_widget_hide( this_->window );
297
298         delete this_;
299 }
300
301 gint tX_midiin::midi_binding_gui::timer( gpointer _this )
302 {
303         tX_midiin::midi_binding_gui* this_ = (tX_midiin::midi_binding_gui*)_this;
304
305         //this_->midi->check_event();
306         
307         tX_midievent tmpevent = this_->midi->get_last_event();
308
309         if( tmpevent.type_matches( this_->last_event ) )
310                 return TRUE;
311         
312         this_->last_event = tmpevent;
313         this_->last_event.clear_non_type();
314
315         snprintf( this_->tempstr, sizeof(this_->tempstr),
316                           "Type: %d (CC=%d, NOTE=%d)\nNumber: %d\nChannel: %d\n",
317                           this_->last_event.type, tX_midievent::CC, tX_midievent::NOTE,
318                           this_->last_event.number,
319                           this_->last_event.channel );
320
321         gtk_label_set_text( GTK_LABEL(this_->midi_event_info), this_->tempstr );
322
323         return TRUE;
324 }
325
326 tX_midiin::midi_binding_gui::~midi_binding_gui ()
327 {
328         gtk_timeout_remove( timer_tag );
329
330         //g_object_unref( window );
331         gtk_widget_destroy( window );
332 }
333
334 #endif // USE_ALSA_MIDI_IN