1387
|
1 /*
|
|
2 *
|
|
3 * Author: Giacomo Lozito <james@develia.org>, (C) 2005-2006
|
|
4 *
|
|
5 * This program is free software; you can redistribute it and/or modify it
|
|
6 * under the terms of the GNU General Public License as published by the
|
|
7 * Free Software Foundation; either version 2 of the License, or (at your
|
|
8 * option) any later version.
|
|
9 *
|
|
10 * This program is distributed in the hope that it will be useful, but
|
|
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
13 * General Public License for more details.
|
|
14 *
|
|
15 * You should have received a copy of the GNU General Public License along
|
|
16 * with this program; if not, write to the Free Software Foundation, Inc.,
|
|
17 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
18 *
|
|
19 */
|
|
20
|
|
21 #include "b-alsa.h"
|
|
22 #include "b-alsa-config.h"
|
|
23
|
|
24 /* sequencer instance */
|
|
25 static sequencer_client_t sc;
|
|
26 /* options */
|
|
27 static amidiplug_cfg_alsa_t amidiplug_cfg_alsa;
|
|
28
|
|
29
|
|
30 gint backend_info_get( gchar ** name , gchar ** longname , gchar ** desc , gint * ppos )
|
|
31 {
|
|
32 if ( name != NULL )
|
|
33 *name = g_strdup( "alsa" );
|
|
34 if ( longname != NULL )
|
|
35 *longname = g_strdup( "ALSA Backend " AMIDIPLUG_VERSION );
|
|
36 if ( desc != NULL )
|
|
37 *desc = g_strdup( _("This backend sends MIDI events to a group of user-chosen "
|
|
38 "ALSA sequencer ports. The ALSA sequencer interface is very "
|
|
39 "versatile, it can provide ports for audio cards hardware "
|
|
40 "synthesizers (i.e. emu10k1) but also for software synths, "
|
|
41 "external devices, etc.\n"
|
|
42 "This backend does not produce audio, MIDI events are handled "
|
|
43 "directly from devices/programs behind the ALSA ports; in example, "
|
|
44 "MIDI events sent to the hardware synth will be directly played.\n"
|
|
45 "Backend written by Giacomo Lozito.") );
|
|
46 if ( ppos != NULL )
|
|
47 *ppos = 1; /* preferred position in backend list */
|
|
48 return 1;
|
|
49 }
|
|
50
|
|
51
|
|
52 gint backend_init( void )
|
|
53 {
|
|
54 /* read configuration options */
|
|
55 i_cfg_read();
|
|
56
|
|
57 sc.seq = NULL;
|
|
58 sc.client_port = 0;
|
|
59 sc.queue = 0;
|
|
60 sc.dest_port = NULL;
|
|
61 sc.dest_port_num = 0;
|
|
62 sc.queue_tempo = NULL;
|
|
63 sc.is_start = FALSE;
|
|
64
|
|
65 return 1;
|
|
66 }
|
|
67
|
|
68 gint backend_cleanup( void )
|
|
69 {
|
|
70 /* free configuration options */
|
|
71 i_cfg_free();
|
|
72
|
|
73 return 1;
|
|
74 }
|
|
75
|
|
76
|
|
77 gint sequencer_get_port_count( void )
|
|
78 {
|
|
79 return i_util_str_count( amidiplug_cfg_alsa.alsa_seq_wports , ':' );
|
|
80 }
|
|
81
|
|
82
|
|
83 gint sequencer_start( gchar * midi_fname )
|
|
84 {
|
|
85 sc.is_start = TRUE;
|
|
86 return 1; /* success */
|
|
87 }
|
|
88
|
|
89
|
|
90 gint sequencer_stop( void )
|
|
91 {
|
|
92 return 1; /* success */
|
|
93 }
|
|
94
|
|
95
|
|
96 /* activate sequencer client */
|
|
97 gint sequencer_on( void )
|
|
98 {
|
|
99 gchar * wports_str = amidiplug_cfg_alsa.alsa_seq_wports;
|
|
100
|
|
101 if ( !i_seq_open() )
|
|
102 {
|
|
103 sc.seq = NULL;
|
|
104 return 0;
|
|
105 }
|
|
106
|
|
107 if ( !i_seq_port_create() )
|
|
108 {
|
|
109 i_seq_close();
|
|
110 sc.seq = NULL;
|
|
111 return 0;
|
|
112 }
|
|
113
|
|
114 if ( !i_seq_queue_create() )
|
|
115 {
|
|
116 i_seq_close();
|
|
117 sc.seq = NULL;
|
|
118 return 0;
|
|
119 }
|
|
120
|
|
121 if (( sc.is_start == TRUE ) && ( wports_str ))
|
|
122 {
|
|
123 sc.is_start = FALSE;
|
|
124 i_seq_port_wparse( wports_str );
|
|
125 }
|
|
126
|
|
127 if ( !i_seq_port_connect() )
|
|
128 {
|
|
129 i_seq_queue_free();
|
|
130 i_seq_close();
|
|
131 sc.seq = NULL;
|
|
132 return 0;
|
|
133 }
|
|
134
|
|
135 /* success */
|
|
136 return 1;
|
|
137 }
|
|
138
|
|
139
|
|
140 /* shutdown sequencer client */
|
|
141 gint sequencer_off( void )
|
|
142 {
|
|
143 if ( sc.seq )
|
|
144 {
|
|
145 i_seq_port_disconnect();
|
|
146 i_seq_queue_free();
|
|
147 i_seq_close();
|
|
148 sc.seq = NULL;
|
|
149 /* return 1 here */
|
|
150 return 1;
|
|
151 }
|
|
152 /* return 2 if it was already freed */
|
|
153 return 2;
|
|
154 }
|
|
155
|
|
156
|
|
157 /* queue set tempo */
|
|
158 gint sequencer_queue_tempo( gint tempo , gint ppq )
|
|
159 {
|
|
160 /* interpret and set tempo */
|
|
161 snd_seq_queue_tempo_alloca( &sc.queue_tempo );
|
|
162 snd_seq_queue_tempo_set_tempo( sc.queue_tempo , tempo );
|
|
163 snd_seq_queue_tempo_set_ppq( sc.queue_tempo , ppq );
|
|
164
|
|
165 if ( snd_seq_set_queue_tempo( sc.seq , sc.queue , sc.queue_tempo ) < 0 )
|
|
166 {
|
|
167 g_warning( "Cannot set queue tempo (%u/%i)\n",
|
|
168 snd_seq_queue_tempo_get_tempo(sc.queue_tempo),
|
|
169 snd_seq_queue_tempo_get_ppq(sc.queue_tempo) );
|
|
170 return 0;
|
|
171 }
|
|
172 return 1;
|
|
173 }
|
|
174
|
|
175
|
|
176 gint sequencer_queue_start( void )
|
|
177 {
|
|
178 return snd_seq_start_queue( sc.seq , sc.queue , NULL );
|
|
179 }
|
|
180
|
|
181
|
|
182 gint sequencer_event_init( void )
|
|
183 {
|
|
184 /* common settings for all our events */
|
|
185 snd_seq_ev_clear(&sc.ev);
|
|
186 sc.ev.queue = sc.queue;
|
|
187 sc.ev.source.port = 0;
|
|
188 sc.ev.flags = SND_SEQ_TIME_STAMP_TICK;
|
|
189 return 1;
|
|
190 }
|
|
191
|
|
192
|
|
193 gint sequencer_event_noteon( midievent_t * event )
|
|
194 {
|
|
195 i_seq_event_common_init( event );
|
|
196 snd_seq_ev_set_fixed(&sc.ev);
|
|
197 sc.ev.data.note.channel = event->data.d[0];
|
|
198 sc.ev.data.note.note = event->data.d[1];
|
|
199 sc.ev.data.note.velocity = event->data.d[2];
|
|
200 return 1;
|
|
201 }
|
|
202
|
|
203
|
|
204 gint sequencer_event_noteoff( midievent_t * event )
|
|
205 {
|
|
206 i_seq_event_common_init( event );
|
|
207 snd_seq_ev_set_fixed(&sc.ev);
|
|
208 sc.ev.data.note.channel = event->data.d[0];
|
|
209 sc.ev.data.note.note = event->data.d[1];
|
|
210 sc.ev.data.note.velocity = event->data.d[2];
|
|
211 return 1;
|
|
212 }
|
|
213
|
|
214
|
|
215 gint sequencer_event_keypress( midievent_t * event )
|
|
216 {
|
|
217 i_seq_event_common_init( event );
|
|
218 snd_seq_ev_set_fixed(&sc.ev);
|
|
219 sc.ev.data.note.channel = event->data.d[0];
|
|
220 sc.ev.data.note.note = event->data.d[1];
|
|
221 sc.ev.data.note.velocity = event->data.d[2];
|
|
222 return 1;
|
|
223 }
|
|
224
|
|
225
|
|
226 gint sequencer_event_controller( midievent_t * event )
|
|
227 {
|
|
228 i_seq_event_common_init( event );
|
|
229 snd_seq_ev_set_fixed(&sc.ev);
|
|
230 sc.ev.data.control.channel = event->data.d[0];
|
|
231 sc.ev.data.control.param = event->data.d[1];
|
|
232 sc.ev.data.control.value = event->data.d[2];
|
|
233 return 1;
|
|
234 }
|
|
235
|
|
236
|
|
237 gint sequencer_event_pgmchange( midievent_t * event )
|
|
238 {
|
|
239 i_seq_event_common_init( event );
|
|
240 snd_seq_ev_set_fixed(&sc.ev);
|
|
241 sc.ev.data.control.channel = event->data.d[0];
|
|
242 sc.ev.data.control.value = event->data.d[1];
|
|
243 return 1;
|
|
244 }
|
|
245
|
|
246
|
|
247 gint sequencer_event_chanpress( midievent_t * event )
|
|
248 {
|
|
249 i_seq_event_common_init( event );
|
|
250 snd_seq_ev_set_fixed(&sc.ev);
|
|
251 sc.ev.data.control.channel = event->data.d[0];
|
|
252 sc.ev.data.control.value = event->data.d[1];
|
|
253 return 1;
|
|
254 }
|
|
255
|
|
256
|
|
257 gint sequencer_event_pitchbend( midievent_t * event )
|
|
258 {
|
|
259 i_seq_event_common_init( event );
|
|
260 snd_seq_ev_set_fixed(&sc.ev);
|
|
261 sc.ev.data.control.channel = event->data.d[0];
|
|
262 sc.ev.data.control.value = ((event->data.d[1]) | ((event->data.d[2]) << 7)) - 0x2000;
|
|
263 return 1;
|
|
264 }
|
|
265
|
|
266
|
|
267 gint sequencer_event_sysex( midievent_t * event )
|
|
268 {
|
|
269 i_seq_event_common_init( event );
|
|
270 snd_seq_ev_set_variable(&sc.ev, event->data.length, event->sysex);
|
|
271 return 1;
|
|
272 }
|
|
273
|
|
274
|
|
275 gint sequencer_event_tempo( midievent_t * event )
|
|
276 {
|
|
277 i_seq_event_common_init( event );
|
|
278 snd_seq_ev_set_fixed(&sc.ev);
|
|
279 sc.ev.dest.client = SND_SEQ_CLIENT_SYSTEM;
|
|
280 sc.ev.dest.port = SND_SEQ_PORT_SYSTEM_TIMER;
|
|
281 sc.ev.data.queue.queue = sc.queue;
|
|
282 sc.ev.data.queue.param.value = event->data.tempo;
|
|
283 return 1;
|
|
284 }
|
|
285
|
|
286
|
|
287 gint sequencer_event_other( midievent_t * event )
|
|
288 {
|
|
289 /* unhandled */
|
|
290 return 1;
|
|
291 }
|
|
292
|
|
293
|
|
294 gint sequencer_output( gpointer * buffer , gint * len )
|
|
295 {
|
|
296 snd_seq_event_output( sc.seq , &sc.ev );
|
|
297 snd_seq_drain_output( sc.seq );
|
|
298 snd_seq_sync_output_queue( sc.seq );
|
|
299 return 0;
|
|
300 }
|
|
301
|
|
302
|
|
303 gint sequencer_output_shut( guint max_tick , gint skip_offset )
|
|
304 {
|
|
305 gint i = 0 , c = 0;
|
|
306 /* time to shutdown playback! */
|
|
307 /* send "ALL SOUNDS OFF" to all channels on all ports */
|
|
308 sc.ev.type = SND_SEQ_EVENT_CONTROLLER;
|
|
309 sc.ev.time.tick = 0;
|
|
310 snd_seq_ev_set_fixed(&sc.ev);
|
|
311 sc.ev.data.control.param = MIDI_CTL_ALL_SOUNDS_OFF;
|
|
312 sc.ev.data.control.value = 0;
|
|
313 for ( i = 0 ; i < sc.dest_port_num ; i++ )
|
|
314 {
|
|
315 sc.ev.queue = sc.queue;
|
|
316 sc.ev.dest = sc.dest_port[i];
|
|
317
|
|
318 for ( c = 0 ; c < 16 ; c++ )
|
|
319 {
|
|
320 sc.ev.data.control.channel = c;
|
|
321 snd_seq_event_output(sc.seq, &sc.ev);
|
|
322 snd_seq_drain_output(sc.seq);
|
|
323 }
|
|
324 }
|
|
325
|
|
326 /* schedule queue stop at end of song */
|
|
327 snd_seq_ev_clear(&sc.ev);
|
|
328 sc.ev.queue = sc.queue;
|
|
329 sc.ev.source.port = 0;
|
|
330 sc.ev.flags = SND_SEQ_TIME_STAMP_TICK;
|
|
331
|
|
332 snd_seq_ev_set_fixed(&sc.ev);
|
|
333 sc.ev.type = SND_SEQ_EVENT_STOP;
|
|
334 sc.ev.time.tick = max_tick - skip_offset;
|
|
335 sc.ev.dest.client = SND_SEQ_CLIENT_SYSTEM;
|
|
336 sc.ev.dest.port = SND_SEQ_PORT_SYSTEM_TIMER;
|
|
337 sc.ev.data.queue.queue = sc.queue;
|
|
338 snd_seq_event_output(sc.seq, &sc.ev);
|
|
339 snd_seq_drain_output(sc.seq);
|
|
340 /* snd_seq_sync_output_queue(sc.seq); */
|
|
341
|
|
342 return 1;
|
|
343 }
|
|
344
|
|
345
|
|
346 gint audio_volume_get( gint * left_volume , gint * right_volume )
|
|
347 {
|
|
348 snd_mixer_t * mixer_h = NULL;
|
|
349 snd_mixer_elem_t * mixer_elem = NULL;
|
|
350 gchar mixer_card[10];
|
|
351 snprintf( mixer_card , 8 , "hw:%i" , amidiplug_cfg_alsa.alsa_mixer_card_id );
|
|
352 mixer_card[9] = '\0';
|
|
353
|
|
354 if ( snd_mixer_open( &mixer_h , 0 ) > -1 )
|
|
355 i_seq_mixer_find_selem( mixer_h , mixer_card ,
|
|
356 amidiplug_cfg_alsa.alsa_mixer_ctl_name ,
|
|
357 amidiplug_cfg_alsa.alsa_mixer_ctl_id ,
|
|
358 &mixer_elem );
|
|
359 else
|
|
360 mixer_h = NULL;
|
|
361
|
|
362 if ( ( mixer_elem ) && ( snd_mixer_selem_has_playback_volume( mixer_elem ) ) )
|
|
363 {
|
|
364 glong pv_min , pv_max , pv_range;
|
|
365 glong lc, rc;
|
|
366
|
|
367 snd_mixer_selem_get_playback_volume_range( mixer_elem , &pv_min , &pv_max );
|
|
368 pv_range = pv_max - pv_min;
|
|
369 if ( pv_range > 0 )
|
|
370 {
|
|
371 if ( snd_mixer_selem_has_playback_channel( mixer_elem , SND_MIXER_SCHN_FRONT_LEFT ) )
|
|
372 {
|
|
373 snd_mixer_selem_get_playback_volume( mixer_elem , SND_MIXER_SCHN_FRONT_LEFT , &lc );
|
|
374 /* convert the range to 0-100 (for the case that pv_range is not 0-100 already) */
|
|
375 *left_volume = (gint)(((lc - pv_min) * 100) / pv_range);
|
|
376 DEBUGMSG( "GET VOLUME requested, get left channel (%i)\n" , *left_volume );
|
|
377 }
|
|
378 if ( snd_mixer_selem_has_playback_channel( mixer_elem , SND_MIXER_SCHN_FRONT_RIGHT ) )
|
|
379 {
|
|
380 snd_mixer_selem_get_playback_volume( mixer_elem , SND_MIXER_SCHN_FRONT_RIGHT , &rc );
|
|
381 /* convert the range to 0-100 (for the case that pv_range is not 0-100 already) */
|
|
382 *right_volume = (gint)(((rc - pv_min) * 100) / pv_range);
|
|
383 DEBUGMSG( "GET VOLUME requested, get right channel (%i)\n" , *right_volume );
|
|
384 }
|
|
385 }
|
|
386 }
|
|
387
|
|
388 if ( mixer_h )
|
|
389 snd_mixer_close( mixer_h );
|
|
390 /* always return 1 here */
|
|
391 return 1;
|
|
392 }
|
|
393
|
|
394
|
|
395 gint audio_volume_set( gint left_volume , gint right_volume )
|
|
396 {
|
|
397 snd_mixer_t * mixer_h = NULL;
|
|
398 snd_mixer_elem_t * mixer_elem = NULL;
|
|
399 gchar mixer_card[10];
|
|
400 snprintf( mixer_card , 8 , "hw:%i" , amidiplug_cfg_alsa.alsa_mixer_card_id );
|
|
401 mixer_card[9] = '\0';
|
|
402
|
|
403 if ( snd_mixer_open( &mixer_h , 0 ) > -1 )
|
|
404 i_seq_mixer_find_selem( mixer_h , mixer_card ,
|
|
405 amidiplug_cfg_alsa.alsa_mixer_ctl_name ,
|
|
406 amidiplug_cfg_alsa.alsa_mixer_ctl_id ,
|
|
407 &mixer_elem );
|
|
408 else
|
|
409 mixer_h = NULL;
|
|
410
|
|
411 if ( ( mixer_elem ) && ( snd_mixer_selem_has_playback_volume( mixer_elem ) ) )
|
|
412 {
|
|
413 glong pv_min , pv_max , pv_range;
|
|
414
|
|
415 snd_mixer_selem_get_playback_volume_range( mixer_elem , &pv_min , &pv_max );
|
|
416 pv_range = pv_max - pv_min;
|
|
417 if ( pv_range > 0 )
|
|
418 {
|
|
419 if ( snd_mixer_selem_has_playback_channel( mixer_elem , SND_MIXER_SCHN_FRONT_LEFT ) )
|
|
420 {
|
|
421 DEBUGMSG( "SET VOLUME requested, setting left channel to %i%%\n" , left_volume );
|
|
422 snd_mixer_selem_set_playback_volume( mixer_elem , SND_MIXER_SCHN_FRONT_LEFT ,
|
|
423 (gint)((gdouble)(0.01 * (gdouble)(left_volume * pv_range)) + pv_min) );
|
|
424 }
|
|
425 if ( snd_mixer_selem_has_playback_channel( mixer_elem , SND_MIXER_SCHN_FRONT_RIGHT ) )
|
|
426 {
|
|
427 DEBUGMSG( "SET VOLUME requested, setting right channel to %i%%\n" , right_volume );
|
|
428 snd_mixer_selem_set_playback_volume( mixer_elem , SND_MIXER_SCHN_FRONT_RIGHT ,
|
|
429 (gint)((gdouble)(0.01 * (gdouble)(right_volume * pv_range)) + pv_min) );
|
|
430 }
|
|
431 }
|
|
432 }
|
|
433
|
|
434 if ( mixer_h )
|
|
435 snd_mixer_close( mixer_h );
|
|
436 /* always return 1 here */
|
|
437 return 1;
|
|
438 }
|
|
439
|
|
440
|
|
441 gint audio_info_get( gint * channels , gint * bitdepth , gint * samplerate )
|
|
442 {
|
|
443 /* not applicable for ALSA backend */
|
|
444 *channels = -1;
|
|
445 *bitdepth = -1;
|
|
446 *samplerate = -1;
|
|
447 return 0; /* not valid information */
|
|
448 }
|
|
449
|
|
450
|
|
451 gboolean audio_check_autonomous( void )
|
|
452 {
|
|
453 return TRUE; /* ALSA deals directly with audio production */
|
|
454 }
|
|
455
|
|
456
|
|
457
|
|
458 /* ******************************************************************
|
|
459 *** EXTRA FUNCTIONS **********************************************
|
|
460 ****************************************************************** */
|
|
461
|
|
462
|
|
463 /* get a list of writable ALSA MIDI ports
|
|
464 use the data_bucket_t here...
|
|
465 bint[0] = client id , bint[1] = port id
|
|
466 bcharp[0] = client name , bcharp[1] = port name
|
|
467 bpointer[0] = (not used) , bpointer[1] = (not used) */
|
|
468 GSList * sequencer_port_get_list( void )
|
|
469 {
|
|
470 snd_seq_t * pseq;
|
|
471 snd_seq_open( &pseq , "default" , SND_SEQ_OPEN_DUPLEX , 0 );
|
|
472
|
|
473 GSList * wports = NULL;
|
|
474 snd_seq_client_info_t *cinfo;
|
|
475 snd_seq_port_info_t *pinfo;
|
|
476
|
|
477 snd_seq_client_info_alloca( &cinfo );
|
|
478 snd_seq_port_info_alloca( &pinfo );
|
|
479
|
|
480 snd_seq_client_info_set_client( cinfo , -1 );
|
|
481 while ( snd_seq_query_next_client( pseq , cinfo ) >= 0 )
|
|
482 {
|
|
483 gint client = snd_seq_client_info_get_client( cinfo );
|
|
484 snd_seq_port_info_set_client( pinfo , client );
|
|
485 snd_seq_port_info_set_port( pinfo , -1 );
|
|
486 while (snd_seq_query_next_port( pseq , pinfo ) >= 0 )
|
|
487 {
|
|
488 if ((snd_seq_port_info_get_capability(pinfo)
|
|
489 & (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE))
|
|
490 == (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE))
|
|
491 {
|
|
492 data_bucket_t * portinfo = (data_bucket_t*)g_malloc(sizeof(data_bucket_t));
|
|
493 portinfo->bint[0] = snd_seq_port_info_get_client( pinfo );
|
|
494 portinfo->bint[1] = snd_seq_port_info_get_port( pinfo );
|
|
495 portinfo->bcharp[0] = g_strdup(snd_seq_client_info_get_name(cinfo));
|
|
496 portinfo->bcharp[1] = g_strdup(snd_seq_port_info_get_name(pinfo));
|
|
497 wports = g_slist_append( wports , portinfo );
|
|
498 }
|
|
499 }
|
|
500 }
|
|
501 /* snd_seq_port_info_free( pinfo );
|
|
502 snd_seq_client_info_free( cinfo ); */
|
|
503 snd_seq_close( pseq );
|
|
504 return wports;
|
|
505 }
|
|
506
|
|
507
|
|
508 void sequencer_port_free_list( GSList * wports )
|
|
509 {
|
|
510 GSList * start = wports;
|
|
511 while ( wports != NULL )
|
|
512 {
|
|
513 data_bucket_t * portinfo = wports->data;
|
|
514 g_free( (gpointer)portinfo->bcharp[0] );
|
|
515 g_free( (gpointer)portinfo->bcharp[1] );
|
|
516 g_free( portinfo );
|
|
517 wports = wports->next;
|
|
518 }
|
|
519 g_slist_free( start );
|
|
520 return;
|
|
521 }
|
|
522
|
|
523
|
|
524 /* get a list of available sound cards and relative mixer controls;
|
|
525 use the data_bucket_t here...
|
|
526 bint[0] = card id , bint[1] = (not used)
|
|
527 bcharp[0] = card name , bcharp[1] = (not used)
|
|
528 bpointer[0] = list (GSList) of mixer controls on the card , bpointer[1] = (not used) */
|
|
529 GSList * alsa_card_get_list( void )
|
|
530 {
|
|
531 gint soundcard_id = -1;
|
|
532 GSList * scards = NULL;
|
|
533
|
|
534 snd_card_next( &soundcard_id );
|
|
535 while ( soundcard_id > -1 )
|
|
536 {
|
|
537 /* card container */
|
|
538 data_bucket_t * cardinfo = (data_bucket_t*)g_malloc(sizeof(data_bucket_t));
|
|
539 cardinfo->bint[0] = soundcard_id;
|
|
540 /* snd_card_get_name calls strdup on its own */
|
|
541 snd_card_get_name( soundcard_id , &cardinfo->bcharp[0] );
|
|
542 /* for each sound card, get a list of available mixer controls */
|
|
543 cardinfo->bpointer[0] = i_seq_mixctl_get_list( soundcard_id );
|
|
544
|
|
545 scards = g_slist_append( scards , cardinfo );
|
|
546 snd_card_next( &soundcard_id );
|
|
547 }
|
|
548 return scards;
|
|
549 }
|
|
550
|
|
551
|
|
552 void alsa_card_free_list( GSList * scards )
|
|
553 {
|
|
554 GSList * start = scards;
|
|
555 while ( scards != NULL )
|
|
556 {
|
|
557 data_bucket_t * cardinfo = scards->data;
|
|
558 /* free the list of mixer controls for the sound card */
|
|
559 i_seq_mixctl_free_list( (GSList*)cardinfo->bpointer[0] );
|
|
560 g_free( (gpointer)cardinfo->bcharp[0] );
|
|
561 g_free( cardinfo );
|
|
562 scards = scards->next;
|
|
563 }
|
|
564 g_slist_free( start );
|
|
565 return;
|
|
566 }
|
|
567
|
|
568
|
|
569
|
|
570 /* ******************************************************************
|
|
571 *** INTERNALS ****************************************************
|
|
572 ****************************************************************** */
|
|
573
|
|
574
|
|
575 /* create sequencer client */
|
|
576 gint i_seq_open( void )
|
|
577 {
|
|
578 gint err;
|
|
579 err = snd_seq_open( &sc.seq , "default" , SND_SEQ_OPEN_DUPLEX , 0 );
|
|
580 if (err < 0)
|
|
581 return 0;
|
|
582 snd_seq_set_client_name( sc.seq , "amidi-plug" );
|
|
583 return 1;
|
|
584 }
|
|
585
|
|
586
|
|
587 /* free sequencer client */
|
|
588 gint i_seq_close( void )
|
|
589 {
|
|
590 if ( snd_seq_close( sc.seq ) < 0 )
|
|
591 return 0; /* fail */
|
|
592 else
|
|
593 return 1; /* success */
|
|
594 }
|
|
595
|
|
596
|
|
597 /* create queue */
|
|
598 gint i_seq_queue_create( void )
|
|
599 {
|
|
600 sc.queue = snd_seq_alloc_named_queue( sc.seq , "AMIDI-Plug" );
|
|
601 if ( sc.queue < 0 )
|
|
602 return 0; /* fail */
|
|
603 else
|
|
604 return 1; /* success */
|
|
605 }
|
|
606
|
|
607
|
|
608 /* free queue */
|
|
609 gint i_seq_queue_free( void )
|
|
610 {
|
|
611 if ( snd_seq_free_queue( sc.seq , sc.queue ) < 0 )
|
|
612 return 0; /* fail */
|
|
613 else
|
|
614 return 1; /* success */
|
|
615 }
|
|
616
|
|
617
|
|
618 /* create sequencer port */
|
|
619 gint i_seq_port_create( void )
|
|
620 {
|
|
621 sc.client_port = snd_seq_create_simple_port( sc.seq , "AMIDI-Plug" , 0 ,
|
|
622 SND_SEQ_PORT_TYPE_MIDI_GENERIC |
|
|
623 SND_SEQ_PORT_TYPE_APPLICATION );
|
|
624 if ( sc.client_port < 0 )
|
|
625 return 0; /* fail */
|
|
626 else
|
|
627 return 1; /* success */
|
|
628 }
|
|
629
|
|
630
|
|
631 /* port connection */
|
|
632 gint i_seq_port_connect( void )
|
|
633 {
|
|
634 gint i = 0 , err = 0;
|
|
635 for ( i = 0 ; i < sc.dest_port_num ; i++ )
|
|
636 {
|
|
637 if ( snd_seq_connect_to( sc.seq , sc.client_port ,
|
|
638 sc.dest_port[i].client ,
|
|
639 sc.dest_port[i].port ) < 0 )
|
|
640 ++err;
|
|
641 }
|
|
642 /* if these values are equal, it means
|
|
643 that all port connections failed */
|
|
644 if ( err == i )
|
|
645 return 0; /* fail */
|
|
646 else
|
|
647 return 1; /* success */
|
|
648 }
|
|
649
|
|
650
|
|
651 /* port disconnection */
|
|
652 gint i_seq_port_disconnect( void )
|
|
653 {
|
|
654 gint i = 0 , err = 0;
|
|
655 for ( i = 0 ; i < sc.dest_port_num ; i++ )
|
|
656 {
|
|
657 if ( snd_seq_disconnect_to( sc.seq , sc.client_port ,
|
|
658 sc.dest_port[i].client ,
|
|
659 sc.dest_port[i].port ) < 0 )
|
|
660 ++err;
|
|
661 }
|
|
662 /* if these values are equal, it means
|
|
663 that all port disconnections failed */
|
|
664 if ( err == i )
|
|
665 return 0; /* fail */
|
|
666 else
|
|
667 return 1; /* success */
|
|
668 }
|
|
669
|
|
670
|
|
671 /* parse writable ports */
|
|
672 gint i_seq_port_wparse( gchar * wportlist )
|
|
673 {
|
|
674 gint i = 0 , err = 0;
|
|
675 gchar **portstr = g_strsplit( wportlist , "," , 0 );
|
|
676
|
|
677 sc.dest_port_num = 0;
|
|
678
|
|
679 /* fill sc.dest_port_num with the writable port number */
|
|
680 while ( portstr[sc.dest_port_num] != NULL )
|
|
681 ++sc.dest_port_num;
|
|
682
|
|
683 /* check if there is already an allocated array and free it */
|
|
684 if ( sc.dest_port )
|
|
685 free( sc.dest_port );
|
|
686
|
|
687 if ( sc.dest_port_num > 0 )
|
|
688 /* allocate the array of writable ports */
|
|
689 sc.dest_port = calloc( sc.dest_port_num , sizeof(snd_seq_addr_t) );
|
|
690
|
|
691 for ( i = 0 ; i < sc.dest_port_num ; i++ )
|
|
692 {
|
|
693 if ( snd_seq_parse_address( sc.seq , &sc.dest_port[i] , portstr[i] ) < 0 )
|
|
694 ++err;
|
|
695 }
|
|
696
|
|
697 g_strfreev( portstr );
|
|
698
|
|
699 /* if these values are equal, it means
|
|
700 that all port translations failed */
|
|
701 if ( err == i )
|
|
702 return 0; /* fail */
|
|
703 else
|
|
704 return 1; /* success */
|
|
705 }
|
|
706
|
|
707
|
|
708 gint i_seq_event_common_init( midievent_t * event )
|
|
709 {
|
|
710 sc.ev.type = event->type;
|
|
711 sc.ev.time.tick = event->tick_real;
|
|
712 sc.ev.dest = sc.dest_port[event->port];
|
|
713 return 1;
|
|
714 }
|
|
715
|
|
716
|
|
717 /* get a list of available mixer controls for a given sound card;
|
|
718 use the data_bucket_t here...
|
|
719 bint[0] = control id , bint[1] = (not used)
|
|
720 bcharp[0] = control name , bcharp[1] = (not used)
|
|
721 bpointer[0] = (not used) , bpointer[1] = (not used) */
|
|
722 GSList * i_seq_mixctl_get_list( gint soundcard_id )
|
|
723 {
|
|
724 GSList * mixctls = NULL;
|
|
725 snd_mixer_t * mixer_h;
|
|
726 snd_mixer_selem_id_t * mixer_selem_id;
|
|
727 snd_mixer_elem_t * mixer_elem;
|
|
728 gchar card[10];
|
|
729
|
|
730 snprintf( card , 8 , "hw:%i" , soundcard_id );
|
|
731 card[9] = '\0';
|
|
732
|
|
733 snd_mixer_selem_id_alloca( &mixer_selem_id );
|
|
734 snd_mixer_open( &mixer_h , 0 );
|
|
735 snd_mixer_attach( mixer_h , card );
|
|
736 snd_mixer_selem_register( mixer_h , NULL , NULL );
|
|
737 snd_mixer_load( mixer_h );
|
|
738 for ( mixer_elem = snd_mixer_first_elem( mixer_h ) ; mixer_elem ;
|
|
739 mixer_elem = snd_mixer_elem_next( mixer_elem ) )
|
|
740 {
|
|
741 data_bucket_t * mixctlinfo = (data_bucket_t*)g_malloc(sizeof(data_bucket_t));
|
|
742 snd_mixer_selem_get_id( mixer_elem , mixer_selem_id );
|
|
743 mixctlinfo->bint[0] = snd_mixer_selem_id_get_index(mixer_selem_id);
|
|
744 mixctlinfo->bcharp[0] = g_strdup(snd_mixer_selem_id_get_name(mixer_selem_id));
|
|
745 mixctls = g_slist_append( mixctls , mixctlinfo );
|
|
746 }
|
|
747 snd_mixer_close( mixer_h );
|
|
748 return mixctls;
|
|
749 }
|
|
750
|
|
751
|
|
752 void i_seq_mixctl_free_list( GSList * mixctls )
|
|
753 {
|
|
754 GSList * start = mixctls;
|
|
755 while ( mixctls != NULL )
|
|
756 {
|
|
757 data_bucket_t * mixctlinfo = mixctls->data;
|
|
758 g_free( (gpointer)mixctlinfo->bcharp[0] );
|
|
759 g_free( mixctlinfo );
|
|
760 mixctls = mixctls->next;
|
|
761 }
|
|
762 g_slist_free( start );
|
|
763 return;
|
|
764 }
|
|
765
|
|
766
|
|
767 gint i_seq_mixer_find_selem( snd_mixer_t * mixer_h , gchar * mixer_card ,
|
|
768 gchar * mixer_control_name , gint mixer_control_id ,
|
|
769 snd_mixer_elem_t ** mixer_elem )
|
|
770 {
|
|
771 snd_mixer_selem_id_t * mixer_selem_id = NULL;
|
|
772 snd_mixer_selem_id_alloca( &mixer_selem_id );
|
|
773 snd_mixer_selem_id_set_index( mixer_selem_id , mixer_control_id );
|
|
774 snd_mixer_selem_id_set_name( mixer_selem_id , mixer_control_name );
|
|
775 snd_mixer_attach( mixer_h , mixer_card );
|
|
776 snd_mixer_selem_register( mixer_h , NULL , NULL);
|
|
777 snd_mixer_load( mixer_h );
|
|
778 /* assign the mixer element (can be NULL if there is no such element) */
|
|
779 *mixer_elem = snd_mixer_find_selem( mixer_h , mixer_selem_id );
|
|
780 /* always return 1 here */
|
|
781 return 1;
|
|
782 }
|
|
783
|
|
784
|
|
785 gchar * i_configure_read_seq_ports_default( void )
|
|
786 {
|
|
787 FILE * fp = NULL;
|
|
788 /* first try, get seq ports from proc on card0 */
|
|
789 fp = fopen( "/proc/asound/card0/wavetableD1" , "rb" );
|
|
790 if ( fp )
|
|
791 {
|
|
792 gchar buffer[100];
|
|
793 while ( !feof( fp ) )
|
|
794 {
|
|
795 fgets( buffer , 100 , fp );
|
|
796 if (( strlen( buffer ) > 11 ) && ( !strncasecmp( buffer , "addresses: " , 11 ) ))
|
|
797 {
|
|
798 /* change spaces between ports (65:0 65:1 65:2 ...)
|
|
799 into commas (65:0,65:1,65:2,...) */
|
|
800 g_strdelimit( &buffer[11] , " " , ',' );
|
|
801 /* remove lf and cr from the end of the string */
|
|
802 g_strdelimit( &buffer[11] , "\r\n" , '\0' );
|
|
803 /* ready to go */
|
|
804 DEBUGMSG( "init, default values for seq ports detected: %s\n" , &buffer[11] );
|
|
805 fclose( fp );
|
|
806 return g_strdup( &buffer[11] );
|
|
807 }
|
|
808 }
|
|
809 fclose( fp );
|
|
810 }
|
|
811
|
|
812 /* second option: do not set ports at all, let the user
|
|
813 select the right ones in the nice config window :) */
|
|
814 return g_strdup( "" );
|
|
815 }
|
|
816
|
|
817
|
|
818 /* count the number of occurrencies of a specific character 'c'
|
|
819 in the string 'string' (it must be a null-terminated string) */
|
|
820 gint i_util_str_count( gchar * string , gchar c )
|
|
821 {
|
|
822 gint i = 0 , count = 0;
|
|
823 while ( string[i] != '\0' )
|
|
824 {
|
|
825 if ( string[i] == c )
|
|
826 ++count;
|
|
827 ++i;
|
|
828 }
|
|
829 return count;
|
|
830 }
|
|
831
|
|
832
|
|
833 void i_cfg_read( void )
|
|
834 {
|
|
835 pcfg_t *cfgfile;
|
|
836 gchar * config_pathfilename = g_strjoin( "" , g_get_home_dir() , "/" ,
|
|
837 PLAYER_LOCALRCDIR , "/amidi-plug.conf" , NULL );
|
|
838 cfgfile = i_pcfg_new_from_file( config_pathfilename );
|
|
839
|
|
840 if ( !cfgfile )
|
|
841 {
|
|
842 /* alsa backend defaults */
|
|
843 amidiplug_cfg_alsa.alsa_seq_wports = i_configure_read_seq_ports_default();
|
|
844 amidiplug_cfg_alsa.alsa_mixer_card_id = 0;
|
|
845 amidiplug_cfg_alsa.alsa_mixer_ctl_name = g_strdup( "Synth" );
|
|
846 amidiplug_cfg_alsa.alsa_mixer_ctl_id = 0;
|
|
847 }
|
|
848 else
|
|
849 {
|
|
850 i_pcfg_read_string( cfgfile , "alsa" , "alsa_seq_wports" ,
|
|
851 &amidiplug_cfg_alsa.alsa_seq_wports , NULL );
|
|
852 if ( amidiplug_cfg_alsa.alsa_seq_wports == NULL )
|
|
853 amidiplug_cfg_alsa.alsa_seq_wports = i_configure_read_seq_ports_default(); /* pick default values */
|
|
854
|
|
855 i_pcfg_read_integer( cfgfile , "alsa" , "alsa_mixer_card_id" ,
|
|
856 &amidiplug_cfg_alsa.alsa_mixer_card_id , 0 );
|
|
857
|
|
858 i_pcfg_read_string( cfgfile , "alsa" , "alsa_mixer_ctl_name" ,
|
|
859 &amidiplug_cfg_alsa.alsa_mixer_ctl_name , "Synth" );
|
|
860
|
|
861 i_pcfg_read_integer( cfgfile , "alsa" , "alsa_mixer_ctl_id" ,
|
|
862 &amidiplug_cfg_alsa.alsa_mixer_ctl_id , 0 );
|
|
863
|
|
864 i_pcfg_free( cfgfile );
|
|
865 }
|
|
866
|
|
867 g_free( config_pathfilename );
|
|
868 }
|
|
869
|
|
870
|
|
871 void i_cfg_free( void )
|
|
872 {
|
|
873 g_free( amidiplug_cfg_alsa.alsa_seq_wports );
|
|
874 g_free( amidiplug_cfg_alsa.alsa_mixer_ctl_name );
|
|
875 }
|