1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*****************************************************************************
4 * This file is part of gmidimonitor
6 * Copyright (C) 2005,2006,2007 Nedko Arnaudov <nedko@arnaudov.name>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 *****************************************************************************/
23 #include <alsa/asoundlib.h>
33 snd_seq_t
* g_seq_ptr
;
34 pthread_t g_alsa_midi_tid
; /* alsa_midi_thread id */
36 /* The ALSA MIDI input handling thread */
38 alsa_midi_thread(void * context_ptr
)
41 snd_seq_event_t
* event_ptr
;
42 GtkListStore
* list_store_ptr
;
43 GtkWidget
* child_ptr
;
44 GString
* time_str_ptr
;
45 GString
* msg_str_ptr
;
46 GString
* channel_str_ptr
;
47 const char * note_name
;
49 const char * drum_name
;
52 child_ptr
= get_glade_widget_child(g_main_window_ptr
, "list");
54 list_store_ptr
= GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(child_ptr
)));
56 while (snd_seq_event_input(g_seq_ptr
, &event_ptr
) >= 0)
61 time_str_ptr
= g_string_new("");
65 (unsigned int)event_ptr
->time
.time
.tv_sec
,
66 (unsigned int)event_ptr
->time
.time
.tv_nsec
);
67 channel_str_ptr
= g_string_new("");
69 /* Workaround for compiler warnings... */
74 if (event_ptr
->type
== SND_SEQ_EVENT_NOTE
||
75 event_ptr
->type
== SND_SEQ_EVENT_NOTEON
||
76 event_ptr
->type
== SND_SEQ_EVENT_NOTEOFF
||
77 event_ptr
->type
== SND_SEQ_EVENT_KEYPRESS
)
82 (unsigned int)event_ptr
->data
.note
.channel
+1);
83 if (event_ptr
->data
.note
.channel
+ 1 == 10)
85 drum_name
= gm_get_drum_name(event_ptr
->data
.note
.note
);
92 note_name
= g_note_names
[event_ptr
->data
.note
.note
% 12];
93 octave
= event_ptr
->data
.note
.note
/ 12 - 1;
96 if (event_ptr
->type
== SND_SEQ_EVENT_CONTROLLER
||
97 event_ptr
->type
== SND_SEQ_EVENT_PGMCHANGE
||
98 event_ptr
->type
== SND_SEQ_EVENT_PITCHBEND
)
103 (unsigned int)event_ptr
->data
.control
.channel
+1);
106 msg_str_ptr
= g_string_new("unknown event");
108 switch (event_ptr
->type
)
110 case SND_SEQ_EVENT_SYSTEM
:
111 g_string_sprintf(msg_str_ptr
, "System event");
113 case SND_SEQ_EVENT_RESULT
:
114 g_string_sprintf(msg_str_ptr
, "Result status event");
116 case SND_SEQ_EVENT_NOTE
:
117 g_string_sprintf(msg_str_ptr
, "Note");
119 case SND_SEQ_EVENT_NOTEON
:
120 if (event_ptr
->data
.note
.velocity
!= 0)
122 if (drum_name
!= NULL
)
126 "Drum: %s (%s, octave %d, velocity %u)",
130 event_ptr
->data
.note
.velocity
);
136 "Note on, %s, octave %d, velocity %u",
139 event_ptr
->data
.note
.velocity
);
143 case SND_SEQ_EVENT_NOTEOFF
:
144 if (drum_name
!= NULL
) /* ignore note off for drums */
149 "Note off, %s, octave %d",
154 case SND_SEQ_EVENT_KEYPRESS
:
155 g_string_sprintf(msg_str_ptr
, "Key pressure change (aftertouch)");
157 case SND_SEQ_EVENT_CONTROLLER
:
159 switch (event_ptr
->data
.control
.param
)
161 case MIDI_CTL_MSB_BANK
:
162 cc_name
= "Bank selection";
164 case MIDI_CTL_MSB_MODWHEEL
:
165 cc_name
= "Modulation";
167 case MIDI_CTL_MSB_BREATH
:
170 case MIDI_CTL_MSB_FOOT
:
173 case MIDI_CTL_MSB_PORTAMENTO_TIME
:
174 cc_name
= "Portamento time";
176 case MIDI_CTL_MSB_DATA_ENTRY
:
177 cc_name
= "Data entry";
179 case MIDI_CTL_MSB_MAIN_VOLUME
:
180 cc_name
= "Main volume";
182 case MIDI_CTL_MSB_BALANCE
:
185 case MIDI_CTL_MSB_PAN
:
188 case MIDI_CTL_MSB_EXPRESSION
:
189 cc_name
= "Expression";
191 case MIDI_CTL_MSB_EFFECT1
:
194 case MIDI_CTL_MSB_EFFECT2
:
197 case MIDI_CTL_MSB_GENERAL_PURPOSE1
:
198 cc_name
= "General purpose 1";
200 case MIDI_CTL_MSB_GENERAL_PURPOSE2
:
201 cc_name
= "General purpose 2";
203 case MIDI_CTL_MSB_GENERAL_PURPOSE3
:
204 cc_name
= "General purpose 3";
206 case MIDI_CTL_MSB_GENERAL_PURPOSE4
:
207 cc_name
= "General purpose 4";
209 case MIDI_CTL_LSB_BANK
:
210 cc_name
= "Bank selection";
212 case MIDI_CTL_LSB_MODWHEEL
:
213 cc_name
= "Modulation";
215 case MIDI_CTL_LSB_BREATH
:
218 case MIDI_CTL_LSB_FOOT
:
221 case MIDI_CTL_LSB_PORTAMENTO_TIME
:
222 cc_name
= "Portamento time";
224 case MIDI_CTL_LSB_DATA_ENTRY
:
225 cc_name
= "Data entry";
227 case MIDI_CTL_LSB_MAIN_VOLUME
:
228 cc_name
= "Main volume";
230 case MIDI_CTL_LSB_BALANCE
:
233 case MIDI_CTL_LSB_PAN
:
236 case MIDI_CTL_LSB_EXPRESSION
:
237 cc_name
= "Expression";
239 case MIDI_CTL_LSB_EFFECT1
:
242 case MIDI_CTL_LSB_EFFECT2
:
245 case MIDI_CTL_LSB_GENERAL_PURPOSE1
:
246 cc_name
= "General purpose 1";
248 case MIDI_CTL_LSB_GENERAL_PURPOSE2
:
249 cc_name
= "General purpose 2";
251 case MIDI_CTL_LSB_GENERAL_PURPOSE3
:
252 cc_name
= "General purpose 3";
254 case MIDI_CTL_LSB_GENERAL_PURPOSE4
:
255 cc_name
= "General purpose 4";
257 case MIDI_CTL_SUSTAIN
:
258 cc_name
= "Sustain pedal";
260 case MIDI_CTL_PORTAMENTO
:
261 cc_name
= "Portamento";
263 case MIDI_CTL_SOSTENUTO
:
264 cc_name
= "Sostenuto";
266 case MIDI_CTL_SOFT_PEDAL
:
267 cc_name
= "Soft pedal";
269 case MIDI_CTL_LEGATO_FOOTSWITCH
:
270 cc_name
= "Legato foot switch";
275 case MIDI_CTL_SC1_SOUND_VARIATION
:
276 cc_name
= "SC1 Sound Variation";
278 case MIDI_CTL_SC2_TIMBRE
:
279 cc_name
= "SC2 Timbre";
281 case MIDI_CTL_SC3_RELEASE_TIME
:
282 cc_name
= "SC3 Release Time";
284 case MIDI_CTL_SC4_ATTACK_TIME
:
285 cc_name
= "SC4 Attack Time";
287 case MIDI_CTL_SC5_BRIGHTNESS
:
288 cc_name
= "SC5 Brightness";
305 case MIDI_CTL_GENERAL_PURPOSE5
:
306 cc_name
= "General purpose 5";
308 case MIDI_CTL_GENERAL_PURPOSE6
:
309 cc_name
= "General purpose 6";
311 case MIDI_CTL_GENERAL_PURPOSE7
:
312 cc_name
= "General purpose 7";
314 case MIDI_CTL_GENERAL_PURPOSE8
:
315 cc_name
= "General purpose 8";
317 case MIDI_CTL_PORTAMENTO_CONTROL
:
318 cc_name
= "Portamento control";
320 case MIDI_CTL_E1_REVERB_DEPTH
:
321 cc_name
= "E1 Reverb Depth";
323 case MIDI_CTL_E2_TREMOLO_DEPTH
:
324 cc_name
= "E2 Tremolo Depth";
326 case MIDI_CTL_E3_CHORUS_DEPTH
:
327 cc_name
= "E3 Chorus Depth";
329 case MIDI_CTL_E4_DETUNE_DEPTH
:
330 cc_name
= "E4 Detune Depth";
332 case MIDI_CTL_E5_PHASER_DEPTH
:
333 cc_name
= "E5 Phaser Depth";
335 case MIDI_CTL_DATA_INCREMENT
:
336 cc_name
= "Data Increment";
338 case MIDI_CTL_DATA_DECREMENT
:
339 cc_name
= "Data Decrement";
341 case MIDI_CTL_NONREG_PARM_NUM_LSB
:
342 cc_name
= "Non-registered parameter number";
344 case MIDI_CTL_NONREG_PARM_NUM_MSB
:
345 cc_name
= "Non-registered parameter number";
347 case MIDI_CTL_REGIST_PARM_NUM_LSB
:
348 cc_name
= "Registered parameter number";
350 case MIDI_CTL_REGIST_PARM_NUM_MSB
:
351 cc_name
= "Registered parameter number";
353 case MIDI_CTL_ALL_SOUNDS_OFF
:
354 cc_name
= "All sounds off";
356 case MIDI_CTL_RESET_CONTROLLERS
:
357 cc_name
= "Reset Controllers";
359 case MIDI_CTL_LOCAL_CONTROL_SWITCH
:
360 cc_name
= "Local control switch";
362 case MIDI_CTL_ALL_NOTES_OFF
:
363 cc_name
= "All notes off";
365 case MIDI_CTL_OMNI_OFF
:
366 cc_name
= "Omni off";
368 case MIDI_CTL_OMNI_ON
:
383 "CC %s (%u), value %u",
385 (unsigned int)event_ptr
->data
.control
.param
,
386 (unsigned int)event_ptr
->data
.control
.value
);
393 (unsigned int)event_ptr
->data
.control
.param
,
394 (unsigned int)event_ptr
->data
.control
.value
);
397 case SND_SEQ_EVENT_PGMCHANGE
:
400 "Program change, %d (%s)",
401 (unsigned int)event_ptr
->data
.control
.value
,
402 event_ptr
->data
.control
.value
> 127 || event_ptr
->data
.control
.value
< 0 ? "???": gm_get_instrument_name(event_ptr
->data
.control
.value
));
404 case SND_SEQ_EVENT_CHANPRESS
:
405 g_string_sprintf(msg_str_ptr
, "Channel pressure");
407 case SND_SEQ_EVENT_PITCHBEND
:
411 (signed int)event_ptr
->data
.control
.value
);
413 case SND_SEQ_EVENT_CONTROL14
:
414 g_string_sprintf(msg_str_ptr
, "14 bit controller value");
416 case SND_SEQ_EVENT_NONREGPARAM
:
417 g_string_sprintf(msg_str_ptr
, "NRPN");
419 case SND_SEQ_EVENT_REGPARAM
:
420 g_string_sprintf(msg_str_ptr
, "RPN");
422 case SND_SEQ_EVENT_SONGPOS
:
423 g_string_sprintf(msg_str_ptr
, "Song position");
425 case SND_SEQ_EVENT_SONGSEL
:
426 g_string_sprintf(msg_str_ptr
, "Song select");
428 case SND_SEQ_EVENT_QFRAME
:
429 g_string_sprintf(msg_str_ptr
, "midi time code quarter frame");
431 case SND_SEQ_EVENT_TIMESIGN
:
432 g_string_sprintf(msg_str_ptr
, "SMF Time Signature event");
434 case SND_SEQ_EVENT_KEYSIGN
:
435 g_string_sprintf(msg_str_ptr
, "SMF Key Signature event");
437 case SND_SEQ_EVENT_START
:
438 g_string_sprintf(msg_str_ptr
, "MIDI Real Time Start message");
440 case SND_SEQ_EVENT_CONTINUE
:
441 g_string_sprintf(msg_str_ptr
, "MIDI Real Time Continue message");
443 case SND_SEQ_EVENT_STOP
:
444 g_string_sprintf(msg_str_ptr
, "MIDI Real Time Stop message");
446 case SND_SEQ_EVENT_SETPOS_TICK
:
447 g_string_sprintf(msg_str_ptr
, "Set tick queue position");
449 case SND_SEQ_EVENT_SETPOS_TIME
:
450 g_string_sprintf(msg_str_ptr
, "Set real-time queue position");
452 case SND_SEQ_EVENT_TEMPO
:
453 g_string_sprintf(msg_str_ptr
, "(SMF) Tempo event");
455 case SND_SEQ_EVENT_CLOCK
:
456 g_string_sprintf(msg_str_ptr
, "MIDI Real Time Clock message");
458 case SND_SEQ_EVENT_TICK
:
459 g_string_sprintf(msg_str_ptr
, "MIDI Real Time Tick message");
461 case SND_SEQ_EVENT_QUEUE_SKEW
:
462 g_string_sprintf(msg_str_ptr
, "Queue timer skew");
464 case SND_SEQ_EVENT_SYNC_POS
:
465 g_string_sprintf(msg_str_ptr
, "Sync position changed");
467 case SND_SEQ_EVENT_TUNE_REQUEST
:
468 g_string_sprintf(msg_str_ptr
, "Tune request");
470 case SND_SEQ_EVENT_RESET
:
471 g_string_sprintf(msg_str_ptr
, "Reset");
473 case SND_SEQ_EVENT_SENSING
:
474 continue; /* disable */
475 g_string_sprintf(msg_str_ptr
, "Active sensing");
477 case SND_SEQ_EVENT_ECHO
:
478 g_string_sprintf(msg_str_ptr
, "Echo-back event");
480 case SND_SEQ_EVENT_OSS
:
481 g_string_sprintf(msg_str_ptr
, "OSS emulation raw event");
483 case SND_SEQ_EVENT_CLIENT_START
:
484 g_string_sprintf(msg_str_ptr
, "New client has connected");
486 case SND_SEQ_EVENT_CLIENT_EXIT
:
487 g_string_sprintf(msg_str_ptr
, "Client has left the system");
489 case SND_SEQ_EVENT_CLIENT_CHANGE
:
490 g_string_sprintf(msg_str_ptr
, "Client status/info has changed");
492 case SND_SEQ_EVENT_PORT_START
:
493 g_string_sprintf(msg_str_ptr
, "New port was created");
495 case SND_SEQ_EVENT_PORT_EXIT
:
496 g_string_sprintf(msg_str_ptr
, "Port was deleted from system");
498 case SND_SEQ_EVENT_PORT_CHANGE
:
499 g_string_sprintf(msg_str_ptr
, "Port status/info has changed");
501 case SND_SEQ_EVENT_PORT_SUBSCRIBED
:
502 g_string_sprintf(msg_str_ptr
, "Port connected");
504 case SND_SEQ_EVENT_PORT_UNSUBSCRIBED
:
505 g_string_sprintf(msg_str_ptr
, "Port disconnected");
507 case SND_SEQ_EVENT_SAMPLE
:
508 g_string_sprintf(msg_str_ptr
, "Sample select");
510 case SND_SEQ_EVENT_SAMPLE_CLUSTER
:
511 g_string_sprintf(msg_str_ptr
, "Sample cluster select");
513 case SND_SEQ_EVENT_SAMPLE_START
:
514 g_string_sprintf(msg_str_ptr
, "voice start");
516 case SND_SEQ_EVENT_SAMPLE_STOP
:
517 g_string_sprintf(msg_str_ptr
, "voice stop");
519 case SND_SEQ_EVENT_SAMPLE_FREQ
:
520 g_string_sprintf(msg_str_ptr
, "playback frequency");
522 case SND_SEQ_EVENT_SAMPLE_VOLUME
:
523 g_string_sprintf(msg_str_ptr
, "volume and balance");
525 case SND_SEQ_EVENT_SAMPLE_LOOP
:
526 g_string_sprintf(msg_str_ptr
, "sample loop");
528 case SND_SEQ_EVENT_SAMPLE_POSITION
:
529 g_string_sprintf(msg_str_ptr
, "sample position");
531 case SND_SEQ_EVENT_SAMPLE_PRIVATE1
:
532 g_string_sprintf(msg_str_ptr
, "private (hardware dependent) event");
534 case SND_SEQ_EVENT_USR0
:
535 g_string_sprintf(msg_str_ptr
, "user-defined event");
537 case SND_SEQ_EVENT_USR1
:
538 g_string_sprintf(msg_str_ptr
, "user-defined event");
540 case SND_SEQ_EVENT_USR2
:
541 g_string_sprintf(msg_str_ptr
, "user-defined event");
543 case SND_SEQ_EVENT_USR3
:
544 g_string_sprintf(msg_str_ptr
, "user-defined event");
546 case SND_SEQ_EVENT_USR4
:
547 g_string_sprintf(msg_str_ptr
, "user-defined event");
549 case SND_SEQ_EVENT_USR5
:
550 g_string_sprintf(msg_str_ptr
, "user-defined event");
552 case SND_SEQ_EVENT_USR6
:
553 g_string_sprintf(msg_str_ptr
, "user-defined event");
555 case SND_SEQ_EVENT_USR7
:
556 g_string_sprintf(msg_str_ptr
, "user-defined event");
558 case SND_SEQ_EVENT_USR8
:
559 g_string_sprintf(msg_str_ptr
, "user-defined event");
561 case SND_SEQ_EVENT_USR9
:
562 g_string_sprintf(msg_str_ptr
, "user-defined event");
564 case SND_SEQ_EVENT_INSTR_BEGIN
:
565 g_string_sprintf(msg_str_ptr
, "begin of instrument management");
567 case SND_SEQ_EVENT_INSTR_END
:
568 g_string_sprintf(msg_str_ptr
, "end of instrument management");
570 case SND_SEQ_EVENT_INSTR_INFO
:
571 g_string_sprintf(msg_str_ptr
, "query instrument interface info");
573 case SND_SEQ_EVENT_INSTR_INFO_RESULT
:
574 g_string_sprintf(msg_str_ptr
, "result of instrument interface info");
576 case SND_SEQ_EVENT_INSTR_FINFO
:
577 g_string_sprintf(msg_str_ptr
, "query instrument format info");
579 case SND_SEQ_EVENT_INSTR_FINFO_RESULT
:
580 g_string_sprintf(msg_str_ptr
, "result of instrument format info");
582 case SND_SEQ_EVENT_INSTR_RESET
:
583 g_string_sprintf(msg_str_ptr
, "reset instrument instrument memory");
585 case SND_SEQ_EVENT_INSTR_STATUS
:
586 g_string_sprintf(msg_str_ptr
, "get instrument interface status");
588 case SND_SEQ_EVENT_INSTR_STATUS_RESULT
:
589 g_string_sprintf(msg_str_ptr
, "result of instrument interface status");
591 case SND_SEQ_EVENT_INSTR_PUT
:
592 g_string_sprintf(msg_str_ptr
, "put an instrument to port");
594 case SND_SEQ_EVENT_INSTR_GET
:
595 g_string_sprintf(msg_str_ptr
, "get an instrument from port");
597 case SND_SEQ_EVENT_INSTR_GET_RESULT
:
598 g_string_sprintf(msg_str_ptr
, "result of instrument query");
600 case SND_SEQ_EVENT_INSTR_FREE
:
601 g_string_sprintf(msg_str_ptr
, "free instrument(s)");
603 case SND_SEQ_EVENT_INSTR_LIST
:
604 g_string_sprintf(msg_str_ptr
, "get instrument list");
606 case SND_SEQ_EVENT_INSTR_LIST_RESULT
:
607 g_string_sprintf(msg_str_ptr
, "result of instrument list");
609 case SND_SEQ_EVENT_INSTR_CLUSTER
:
610 g_string_sprintf(msg_str_ptr
, "set cluster parameters");
612 case SND_SEQ_EVENT_INSTR_CLUSTER_GET
:
613 g_string_sprintf(msg_str_ptr
, "get cluster parameters");
615 case SND_SEQ_EVENT_INSTR_CLUSTER_RESULT
:
616 g_string_sprintf(msg_str_ptr
, "result of cluster parameters");
618 case SND_SEQ_EVENT_INSTR_CHANGE
:
619 g_string_sprintf(msg_str_ptr
, "instrument change");
621 case SND_SEQ_EVENT_SYSEX
:
623 (guint8
*)event_ptr
->data
.ext
.ptr
,
624 event_ptr
->data
.ext
.len
,
627 case SND_SEQ_EVENT_BOUNCE
:
628 g_string_sprintf(msg_str_ptr
, "error event");
630 case SND_SEQ_EVENT_USR_VAR0
:
631 g_string_sprintf(msg_str_ptr
, "reserved for user apps");
633 case SND_SEQ_EVENT_USR_VAR1
:
634 g_string_sprintf(msg_str_ptr
, "reserved for user apps");
636 case SND_SEQ_EVENT_USR_VAR2
:
637 g_string_sprintf(msg_str_ptr
, "reserved for user apps");
639 case SND_SEQ_EVENT_USR_VAR3
:
640 g_string_sprintf(msg_str_ptr
, "reserved for user apps");
642 case SND_SEQ_EVENT_USR_VAR4
:
643 g_string_sprintf(msg_str_ptr
, "reserved for user apps");
647 /* get GTK thread lock */
650 if (g_row_count
>= MAX_LIST_SIZE
)
652 gtk_tree_model_get_iter_first(
653 GTK_TREE_MODEL(list_store_ptr
),
656 gtk_list_store_remove(
661 /* Append an empty row to the list store. Iter will point to the new row */
662 gtk_list_store_append(list_store_ptr
, &iter
);
667 COL_TIME
, time_str_ptr
->str
,
668 COL_CHANNEL
, channel_str_ptr
->str
,
669 COL_MESSAGE
, msg_str_ptr
->str
,
672 gtk_tree_view_scroll_to_cell(
673 GTK_TREE_VIEW(child_ptr
),
674 gtk_tree_model_get_path(
675 gtk_tree_view_get_model(GTK_TREE_VIEW(child_ptr
)),
682 /* Force update of scroll position. */
683 /* Is it a bug that it does not update automagically ? */
684 gtk_container_check_resize(GTK_CONTAINER(child_ptr
));
688 /* release GTK thread lock */
691 g_string_free(channel_str_ptr
, TRUE
);
692 g_string_free(msg_str_ptr
, TRUE
);
693 g_string_free(time_str_ptr
, TRUE
);
700 alsa_init(const char * name
)
703 snd_seq_port_info_t
* port_info
= NULL
;
712 g_warning("Cannot open sequncer, %s\n", snd_strerror(ret
));
716 snd_seq_set_client_name(g_seq_ptr
, name
);
719 lash_alsa_client_id(g_lashc
, snd_seq_client_id(g_seq_ptr
));
722 snd_seq_port_info_alloca(&port_info
);
724 snd_seq_port_info_set_capability(
726 SND_SEQ_PORT_CAP_WRITE
|
727 SND_SEQ_PORT_CAP_SUBS_WRITE
);
728 snd_seq_port_info_set_type(
730 SND_SEQ_PORT_TYPE_APPLICATION
);
731 snd_seq_port_info_set_midi_channels(port_info
, 16);
732 snd_seq_port_info_set_port_specified(port_info
, 1);
734 snd_seq_port_info_set_name(port_info
, "midi in");
735 snd_seq_port_info_set_port(port_info
, 0);
737 ret
= snd_seq_create_port(g_seq_ptr
, port_info
);
740 g_warning("Error creating ALSA sequencer port, %s\n", snd_strerror(ret
));
744 /* Start midi thread */
745 ret
= pthread_create(&g_alsa_midi_tid
, NULL
, alsa_midi_thread
, NULL
);
750 ret
= snd_seq_close(g_seq_ptr
);
753 g_warning("Cannot close sequncer, %s\n", snd_strerror(ret
));
765 /* Cancel the thread. Don't know better way.
766 Poll or unblock mechanisms seem to not be
767 available for alsa sequencer */
768 pthread_cancel(g_alsa_midi_tid
);
770 /* Wait midi thread to finish */
771 ret
= pthread_join(g_alsa_midi_tid
, NULL
);
773 ret
= snd_seq_close(g_seq_ptr
);
776 g_warning("Cannot close sequncer, %s\n", snd_strerror(ret
));