1 #ifdef _HAVE_ALSA_
2 /*
3 * alsabackend.c
4 * ALSA sequencer MIDI backend.
5 *
6 * for Denemo, a gtk+ frontend to GNU Lilypond
7 * Copyright (C) 2011 Dominic Sacré
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 */
14
15 #include "audio/alsabackend.h"
16 #include "audio/midi.h"
17
18 #include <alsa/asoundlib.h>
19 #include <glib.h>
20
21
22 static char const *ALSA_SEQ_CLIENT_NAME = "denemo";
23
24 static int const PLAYBACK_INTERVAL = 5000;
25
26
27 static snd_seq_t *seq;
28
29 static int in_port_id;
30 static int out_port_id;
31
32 static snd_midi_event_t *parser;
33
34 static GThread *process_thread;
35 static GCond *process_cond;
36 static gboolean quit_thread = FALSE;
37
38 static gboolean reset = FALSE;
39
40 static double playback_start_time;
41
42
43 static gpointer
process_thread_func(gpointer data)44 process_thread_func (gpointer data)
45 {
46 GMutex *mutex = g_mutex_new ();
47 gint64 end_time;
48 g_mutex_lock (mutex);
49
50 for (;;)
51 {
52 end_time = g_get_monotonic_time () + (PLAYBACK_INTERVAL * G_TIME_SPAN_SECOND)/1000000;
53 g_cond_wait_until (process_cond, mutex, end_time);
54 if (g_atomic_int_get (&quit_thread))
55 {
56 break;
57 }
58
59 snd_seq_event_t *pev;
60 while (snd_seq_event_input (seq, &pev) >= 0)
61 {
62 unsigned char buffer[3];
63
64 snd_midi_event_reset_decode (parser);
65 if (snd_midi_event_decode (parser, buffer, sizeof (buffer), pev) > 0)
66 {
67 input_midi_event (MIDI_BACKEND, 0, buffer);
68 }
69 }
70
71
72 GTimeVal tv;
73 g_get_current_time (&tv);
74 double now = (double) tv.tv_sec + tv.tv_usec / 1000000.0;
75 double playback_time = now - playback_start_time;
76
77 unsigned char event_data[3];
78 size_t event_length;
79 double event_time;
80
81 double until_time = playback_time + PLAYBACK_INTERVAL / 1000000.0;
82
83 if (reset)
84 {
85 int n;
86 for (n = 0; n < 16; ++n)
87 {
88 snd_seq_event_t ev;
89 snd_seq_ev_set_controller (&ev, n, 123, 0);
90 snd_seq_ev_set_subs (&ev);
91 snd_seq_ev_set_direct (&ev);
92 snd_seq_ev_set_source (&ev, out_port_id);
93 snd_seq_event_output_direct (seq, &ev);
94 }
95 reset = FALSE;
96 }
97
98 while (read_event_from_queue (MIDI_BACKEND, event_data, &event_length, &event_time, until_time))
99 {
100 snd_seq_event_t ev;
101
102 snd_midi_event_reset_encode (parser);
103 snd_midi_event_encode (parser, event_data, event_length, &ev);
104
105 snd_seq_ev_set_subs (&ev);
106 snd_seq_ev_set_direct (&ev);
107 snd_seq_ev_set_source (&ev, out_port_id);
108
109 snd_seq_event_output_direct (seq, &ev);
110 }
111
112 if (is_playing ())
113 {
114 update_playback_time (TIMEBASE_PRIO_MIDI, playback_time);
115 }
116 }
117 g_mutex_free (mutex);
118 return NULL;
119 }
120
121
122
123 static int
alsa_seq_initialize(DenemoPrefs * config)124 alsa_seq_initialize (DenemoPrefs * config)
125 {
126 g_message ("Initializing ALSA sequencer MIDI backend");
127
128 // create sequencer client
129 if (snd_seq_open (&seq, "hw", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK) < 0)
130 {
131 g_warning ("error opening alsa sequencer");
132 return -1;
133 }
134
135 snd_seq_set_client_name (seq, ALSA_SEQ_CLIENT_NAME);
136
137 // create input port
138 in_port_id = snd_seq_create_simple_port (seq, "midi_in", SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_APPLICATION);
139 if (in_port_id < 0)
140 {
141 g_warning ("error creating sequencer output port");
142 return -1;
143 }
144
145 // create output port
146 out_port_id = snd_seq_create_simple_port (seq, "midi_out", SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ, SND_SEQ_PORT_TYPE_APPLICATION);
147
148 if (in_port_id < 0)
149 {
150 g_warning ("error creating sequencer output port");
151 return -1;
152 }
153
154 // initialize MIDI event parser
155 if (snd_midi_event_new (12, &parser))
156 {
157 g_warning ("error initializing MIDI event parser");
158 return -1;
159 }
160 snd_midi_event_init (parser);
161 snd_midi_event_no_status (parser, 1);
162
163
164 process_cond = g_cond_new ();
165
166 process_thread = g_thread_try_new ("ALSA process", process_thread_func, NULL, NULL);
167
168 return 0;
169 }
170
171
172 static int
alsa_seq_destroy()173 alsa_seq_destroy ()
174 {
175 g_message ("Destroying ALSA sequencer MIDI backend");
176
177 g_atomic_int_set (&quit_thread, TRUE);
178 g_cond_signal (process_cond);
179 g_thread_join (process_thread);
180
181 g_cond_free (process_cond);
182
183
184 snd_midi_event_free (parser);
185
186 snd_seq_delete_port (seq, in_port_id);
187 snd_seq_delete_port (seq, out_port_id);
188
189 snd_seq_close (seq);
190
191 return 0;
192 }
193
194
195 static int
alsa_seq_reconfigure(DenemoPrefs * config)196 alsa_seq_reconfigure (DenemoPrefs * config)
197 {
198 alsa_seq_destroy ();
199 return alsa_seq_initialize (config);
200 }
201
202
203 static int
alsa_seq_start_playing()204 alsa_seq_start_playing ()
205 {
206 GTimeVal tv;
207 g_get_current_time (&tv);
208 playback_start_time = (double) tv.tv_sec + tv.tv_usec / 1000000.0;
209 playback_start_time -= get_playback_time ();
210 return 0;
211 }
212
213
214 static int
alsa_seq_stop_playing()215 alsa_seq_stop_playing ()
216 {
217 reset = TRUE;
218 return 0;
219 }
220
221
222 static int
alsa_seq_panic()223 alsa_seq_panic ()
224 {
225 reset = TRUE;
226 return 0;
227 }
228
229
230 backend_t alsa_seq_midi_backend = {
231 alsa_seq_initialize,
232 alsa_seq_destroy,
233 alsa_seq_reconfigure,
234 alsa_seq_start_playing,
235 alsa_seq_stop_playing,
236 alsa_seq_panic,
237 };
238 #endif //_HAVE_ALSA_
239