comparison src/amidi-plug/backend-alsa/b-alsa.c @ 12:3da1b8942b8b trunk

[svn] - remove src/Input src/Output src/Effect src/General src/Visualization src/Container
author nenolod
date Mon, 18 Sep 2006 03:14:20 -0700
parents src/Input/amidi-plug/backend-alsa/b-alsa.c@13389e613d67
children e1c6b223431e
comparison
equal deleted inserted replaced
11:cff1d04026ae 12:3da1b8942b8b
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 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 gint err;
471 snd_seq_t * pseq;
472 err = snd_seq_open( &pseq , "default" , SND_SEQ_OPEN_DUPLEX , 0 );
473 if ( err < 0 )
474 return NULL;
475
476 GSList * wports = NULL;
477 snd_seq_client_info_t *cinfo;
478 snd_seq_port_info_t *pinfo;
479
480 snd_seq_client_info_alloca( &cinfo );
481 snd_seq_port_info_alloca( &pinfo );
482
483 snd_seq_client_info_set_client( cinfo , -1 );
484 while ( snd_seq_query_next_client( pseq , cinfo ) >= 0 )
485 {
486 gint client = snd_seq_client_info_get_client( cinfo );
487 snd_seq_port_info_set_client( pinfo , client );
488 snd_seq_port_info_set_port( pinfo , -1 );
489 while (snd_seq_query_next_port( pseq , pinfo ) >= 0 )
490 {
491 if ((snd_seq_port_info_get_capability(pinfo)
492 & (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE))
493 == (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE))
494 {
495 data_bucket_t * portinfo = (data_bucket_t*)g_malloc(sizeof(data_bucket_t));
496 portinfo->bint[0] = snd_seq_port_info_get_client( pinfo );
497 portinfo->bint[1] = snd_seq_port_info_get_port( pinfo );
498 portinfo->bcharp[0] = g_strdup(snd_seq_client_info_get_name(cinfo));
499 portinfo->bcharp[1] = g_strdup(snd_seq_port_info_get_name(pinfo));
500 wports = g_slist_append( wports , portinfo );
501 }
502 }
503 }
504 /* snd_seq_port_info_free( pinfo );
505 snd_seq_client_info_free( cinfo ); */
506 snd_seq_close( pseq );
507 return wports;
508 }
509
510
511 void sequencer_port_free_list( GSList * wports )
512 {
513 GSList * start = wports;
514 while ( wports != NULL )
515 {
516 data_bucket_t * portinfo = wports->data;
517 g_free( (gpointer)portinfo->bcharp[0] );
518 g_free( (gpointer)portinfo->bcharp[1] );
519 g_free( portinfo );
520 wports = wports->next;
521 }
522 g_slist_free( start );
523 return;
524 }
525
526
527 /* get a list of available sound cards and relative mixer controls;
528 use the data_bucket_t here...
529 bint[0] = card id , bint[1] = (not used)
530 bcharp[0] = card name , bcharp[1] = (not used)
531 bpointer[0] = list (GSList) of mixer controls on the card , bpointer[1] = (not used) */
532 GSList * alsa_card_get_list( void )
533 {
534 gint soundcard_id = -1;
535 GSList * scards = NULL;
536
537 snd_card_next( &soundcard_id );
538 while ( soundcard_id > -1 )
539 {
540 /* card container */
541 data_bucket_t * cardinfo = (data_bucket_t*)g_malloc(sizeof(data_bucket_t));
542 cardinfo->bint[0] = soundcard_id;
543 /* snd_card_get_name calls strdup on its own */
544 snd_card_get_name( soundcard_id , &cardinfo->bcharp[0] );
545 /* for each sound card, get a list of available mixer controls */
546 cardinfo->bpointer[0] = i_seq_mixctl_get_list( soundcard_id );
547
548 scards = g_slist_append( scards , cardinfo );
549 snd_card_next( &soundcard_id );
550 }
551 return scards;
552 }
553
554
555 void alsa_card_free_list( GSList * scards )
556 {
557 GSList * start = scards;
558 while ( scards != NULL )
559 {
560 data_bucket_t * cardinfo = scards->data;
561 /* free the list of mixer controls for the sound card */
562 i_seq_mixctl_free_list( (GSList*)cardinfo->bpointer[0] );
563 g_free( (gpointer)cardinfo->bcharp[0] );
564 g_free( cardinfo );
565 scards = scards->next;
566 }
567 g_slist_free( start );
568 return;
569 }
570
571
572
573 /* ******************************************************************
574 *** INTERNALS ****************************************************
575 ****************************************************************** */
576
577
578 /* create sequencer client */
579 gint i_seq_open( void )
580 {
581 gint err;
582 err = snd_seq_open( &sc.seq , "default" , SND_SEQ_OPEN_DUPLEX , 0 );
583 if (err < 0)
584 return 0;
585 snd_seq_set_client_name( sc.seq , "amidi-plug" );
586 return 1;
587 }
588
589
590 /* free sequencer client */
591 gint i_seq_close( void )
592 {
593 if ( snd_seq_close( sc.seq ) < 0 )
594 return 0; /* fail */
595 else
596 return 1; /* success */
597 }
598
599
600 /* create queue */
601 gint i_seq_queue_create( void )
602 {
603 sc.queue = snd_seq_alloc_named_queue( sc.seq , "AMIDI-Plug" );
604 if ( sc.queue < 0 )
605 return 0; /* fail */
606 else
607 return 1; /* success */
608 }
609
610
611 /* free queue */
612 gint i_seq_queue_free( void )
613 {
614 if ( snd_seq_free_queue( sc.seq , sc.queue ) < 0 )
615 return 0; /* fail */
616 else
617 return 1; /* success */
618 }
619
620
621 /* create sequencer port */
622 gint i_seq_port_create( void )
623 {
624 sc.client_port = snd_seq_create_simple_port( sc.seq , "AMIDI-Plug" , 0 ,
625 SND_SEQ_PORT_TYPE_MIDI_GENERIC |
626 SND_SEQ_PORT_TYPE_APPLICATION );
627 if ( sc.client_port < 0 )
628 return 0; /* fail */
629 else
630 return 1; /* success */
631 }
632
633
634 /* port connection */
635 gint i_seq_port_connect( void )
636 {
637 gint i = 0 , err = 0;
638 for ( i = 0 ; i < sc.dest_port_num ; i++ )
639 {
640 if ( snd_seq_connect_to( sc.seq , sc.client_port ,
641 sc.dest_port[i].client ,
642 sc.dest_port[i].port ) < 0 )
643 ++err;
644 }
645 /* if these values are equal, it means
646 that all port connections failed */
647 if ( err == i )
648 return 0; /* fail */
649 else
650 return 1; /* success */
651 }
652
653
654 /* port disconnection */
655 gint i_seq_port_disconnect( void )
656 {
657 gint i = 0 , err = 0;
658 for ( i = 0 ; i < sc.dest_port_num ; i++ )
659 {
660 if ( snd_seq_disconnect_to( sc.seq , sc.client_port ,
661 sc.dest_port[i].client ,
662 sc.dest_port[i].port ) < 0 )
663 ++err;
664 }
665 /* if these values are equal, it means
666 that all port disconnections failed */
667 if ( err == i )
668 return 0; /* fail */
669 else
670 return 1; /* success */
671 }
672
673
674 /* parse writable ports */
675 gint i_seq_port_wparse( gchar * wportlist )
676 {
677 gint i = 0 , err = 0;
678 gchar **portstr = g_strsplit( wportlist , "," , 0 );
679
680 sc.dest_port_num = 0;
681
682 /* fill sc.dest_port_num with the writable port number */
683 while ( portstr[sc.dest_port_num] != NULL )
684 ++sc.dest_port_num;
685
686 /* check if there is already an allocated array and free it */
687 if ( sc.dest_port )
688 free( sc.dest_port );
689
690 if ( sc.dest_port_num > 0 )
691 /* allocate the array of writable ports */
692 sc.dest_port = calloc( sc.dest_port_num , sizeof(snd_seq_addr_t) );
693
694 for ( i = 0 ; i < sc.dest_port_num ; i++ )
695 {
696 if ( snd_seq_parse_address( sc.seq , &sc.dest_port[i] , portstr[i] ) < 0 )
697 ++err;
698 }
699
700 g_strfreev( portstr );
701
702 /* if these values are equal, it means
703 that all port translations failed */
704 if ( err == i )
705 return 0; /* fail */
706 else
707 return 1; /* success */
708 }
709
710
711 gint i_seq_event_common_init( midievent_t * event )
712 {
713 sc.ev.type = event->type;
714 sc.ev.time.tick = event->tick_real;
715 sc.ev.dest = sc.dest_port[event->port];
716 return 1;
717 }
718
719
720 /* get a list of available mixer controls for a given sound card;
721 use the data_bucket_t here...
722 bint[0] = control id , bint[1] = (not used)
723 bcharp[0] = control name , bcharp[1] = (not used)
724 bpointer[0] = (not used) , bpointer[1] = (not used) */
725 GSList * i_seq_mixctl_get_list( gint soundcard_id )
726 {
727 GSList * mixctls = NULL;
728 snd_mixer_t * mixer_h;
729 snd_mixer_selem_id_t * mixer_selem_id;
730 snd_mixer_elem_t * mixer_elem;
731 gchar card[10];
732
733 snprintf( card , 8 , "hw:%i" , soundcard_id );
734 card[9] = '\0';
735
736 snd_mixer_selem_id_alloca( &mixer_selem_id );
737 snd_mixer_open( &mixer_h , 0 );
738 snd_mixer_attach( mixer_h , card );
739 snd_mixer_selem_register( mixer_h , NULL , NULL );
740 snd_mixer_load( mixer_h );
741 for ( mixer_elem = snd_mixer_first_elem( mixer_h ) ; mixer_elem ;
742 mixer_elem = snd_mixer_elem_next( mixer_elem ) )
743 {
744 data_bucket_t * mixctlinfo = (data_bucket_t*)g_malloc(sizeof(data_bucket_t));
745 snd_mixer_selem_get_id( mixer_elem , mixer_selem_id );
746 mixctlinfo->bint[0] = snd_mixer_selem_id_get_index(mixer_selem_id);
747 mixctlinfo->bcharp[0] = g_strdup(snd_mixer_selem_id_get_name(mixer_selem_id));
748 mixctls = g_slist_append( mixctls , mixctlinfo );
749 }
750 snd_mixer_close( mixer_h );
751 return mixctls;
752 }
753
754
755 void i_seq_mixctl_free_list( GSList * mixctls )
756 {
757 GSList * start = mixctls;
758 while ( mixctls != NULL )
759 {
760 data_bucket_t * mixctlinfo = mixctls->data;
761 g_free( (gpointer)mixctlinfo->bcharp[0] );
762 g_free( mixctlinfo );
763 mixctls = mixctls->next;
764 }
765 g_slist_free( start );
766 return;
767 }
768
769
770 gint i_seq_mixer_find_selem( snd_mixer_t * mixer_h , gchar * mixer_card ,
771 gchar * mixer_control_name , gint mixer_control_id ,
772 snd_mixer_elem_t ** mixer_elem )
773 {
774 snd_mixer_selem_id_t * mixer_selem_id = NULL;
775 snd_mixer_selem_id_alloca( &mixer_selem_id );
776 snd_mixer_selem_id_set_index( mixer_selem_id , mixer_control_id );
777 snd_mixer_selem_id_set_name( mixer_selem_id , mixer_control_name );
778 snd_mixer_attach( mixer_h , mixer_card );
779 snd_mixer_selem_register( mixer_h , NULL , NULL);
780 snd_mixer_load( mixer_h );
781 /* assign the mixer element (can be NULL if there is no such element) */
782 *mixer_elem = snd_mixer_find_selem( mixer_h , mixer_selem_id );
783 /* always return 1 here */
784 return 1;
785 }
786
787
788 gchar * i_configure_read_seq_ports_default( void )
789 {
790 FILE * fp = NULL;
791 /* first try, get seq ports from proc on card0 */
792 fp = fopen( "/proc/asound/card0/wavetableD1" , "rb" );
793 if ( fp )
794 {
795 gchar buffer[100];
796 while ( !feof( fp ) )
797 {
798 fgets( buffer , 100 , fp );
799 if (( strlen( buffer ) > 11 ) && ( !strncasecmp( buffer , "addresses: " , 11 ) ))
800 {
801 /* change spaces between ports (65:0 65:1 65:2 ...)
802 into commas (65:0,65:1,65:2,...) */
803 g_strdelimit( &buffer[11] , " " , ',' );
804 /* remove lf and cr from the end of the string */
805 g_strdelimit( &buffer[11] , "\r\n" , '\0' );
806 /* ready to go */
807 DEBUGMSG( "init, default values for seq ports detected: %s\n" , &buffer[11] );
808 fclose( fp );
809 return g_strdup( &buffer[11] );
810 }
811 }
812 fclose( fp );
813 }
814
815 /* second option: do not set ports at all, let the user
816 select the right ones in the nice config window :) */
817 return g_strdup( "" );
818 }
819
820
821 /* count the number of occurrencies of a specific character 'c'
822 in the string 'string' (it must be a null-terminated string) */
823 gint i_util_str_count( gchar * string , gchar c )
824 {
825 gint i = 0 , count = 0;
826 while ( string[i] != '\0' )
827 {
828 if ( string[i] == c )
829 ++count;
830 ++i;
831 }
832 return count;
833 }
834
835
836 void i_cfg_read( void )
837 {
838 pcfg_t *cfgfile;
839 gchar * config_pathfilename = g_strjoin( "" , g_get_home_dir() , "/" ,
840 PLAYER_LOCALRCDIR , "/amidi-plug.conf" , NULL );
841 cfgfile = i_pcfg_new_from_file( config_pathfilename );
842
843 if ( !cfgfile )
844 {
845 /* alsa backend defaults */
846 amidiplug_cfg_alsa.alsa_seq_wports = i_configure_read_seq_ports_default();
847 amidiplug_cfg_alsa.alsa_mixer_card_id = 0;
848 amidiplug_cfg_alsa.alsa_mixer_ctl_name = g_strdup( "Synth" );
849 amidiplug_cfg_alsa.alsa_mixer_ctl_id = 0;
850 }
851 else
852 {
853 i_pcfg_read_string( cfgfile , "alsa" , "alsa_seq_wports" ,
854 &amidiplug_cfg_alsa.alsa_seq_wports , NULL );
855 if ( amidiplug_cfg_alsa.alsa_seq_wports == NULL )
856 amidiplug_cfg_alsa.alsa_seq_wports = i_configure_read_seq_ports_default(); /* pick default values */
857
858 i_pcfg_read_integer( cfgfile , "alsa" , "alsa_mixer_card_id" ,
859 &amidiplug_cfg_alsa.alsa_mixer_card_id , 0 );
860
861 i_pcfg_read_string( cfgfile , "alsa" , "alsa_mixer_ctl_name" ,
862 &amidiplug_cfg_alsa.alsa_mixer_ctl_name , "Synth" );
863
864 i_pcfg_read_integer( cfgfile , "alsa" , "alsa_mixer_ctl_id" ,
865 &amidiplug_cfg_alsa.alsa_mixer_ctl_id , 0 );
866
867 i_pcfg_free( cfgfile );
868 }
869
870 g_free( config_pathfilename );
871 }
872
873
874 void i_cfg_free( void )
875 {
876 g_free( amidiplug_cfg_alsa.alsa_seq_wports );
877 g_free( amidiplug_cfg_alsa.alsa_mixer_ctl_name );
878 }