Mercurial > audlegacy-plugins
diff src/Input/amidi-plug/backend-alsa/b-alsa.c @ 0:13389e613d67 trunk
[svn] - initial import of audacious-plugins tree (lots to do)
author | nenolod |
---|---|
date | Mon, 18 Sep 2006 01:11:49 -0700 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Input/amidi-plug/backend-alsa/b-alsa.c Mon Sep 18 01:11:49 2006 -0700 @@ -0,0 +1,878 @@ +/* +* +* Author: Giacomo Lozito <james@develia.org>, (C) 2005-2006 +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +* +*/ + +#include "b-alsa.h" +#include "b-alsa-config.h" + +/* sequencer instance */ +static sequencer_client_t sc; +/* options */ +static amidiplug_cfg_alsa_t amidiplug_cfg_alsa; + + +gint backend_info_get( gchar ** name , gchar ** longname , gchar ** desc , gint * ppos ) +{ + if ( name != NULL ) + *name = g_strdup( "alsa" ); + if ( longname != NULL ) + *longname = g_strdup( "ALSA Backend " AMIDIPLUG_VERSION ); + if ( desc != NULL ) + *desc = g_strdup( _("This backend sends MIDI events to a group of user-chosen " + "ALSA sequencer ports. The ALSA sequencer interface is very " + "versatile, it can provide ports for audio cards hardware " + "synthesizers (i.e. emu10k1) but also for software synths, " + "external devices, etc.\n" + "This backend does not produce audio, MIDI events are handled " + "directly from devices/programs behind the ALSA ports; in example, " + "MIDI events sent to the hardware synth will be directly played.\n" + "Backend written by Giacomo Lozito.") ); + if ( ppos != NULL ) + *ppos = 1; /* preferred position in backend list */ + return 1; +} + + +gint backend_init( void ) +{ + /* read configuration options */ + i_cfg_read(); + + sc.seq = NULL; + sc.client_port = 0; + sc.queue = 0; + sc.dest_port = NULL; + sc.dest_port_num = 0; + sc.queue_tempo = NULL; + sc.is_start = FALSE; + + return 1; +} + +gint backend_cleanup( void ) +{ + /* free configuration options */ + i_cfg_free(); + + return 1; +} + + +gint sequencer_get_port_count( void ) +{ + return i_util_str_count( amidiplug_cfg_alsa.alsa_seq_wports , ':' ); +} + + +gint sequencer_start( gchar * midi_fname ) +{ + sc.is_start = TRUE; + return 1; /* success */ +} + + +gint sequencer_stop( void ) +{ + return 1; /* success */ +} + + +/* activate sequencer client */ +gint sequencer_on( void ) +{ + gchar * wports_str = amidiplug_cfg_alsa.alsa_seq_wports; + + if ( !i_seq_open() ) + { + sc.seq = NULL; + return 0; + } + + if ( !i_seq_port_create() ) + { + i_seq_close(); + sc.seq = NULL; + return 0; + } + + if ( !i_seq_queue_create() ) + { + i_seq_close(); + sc.seq = NULL; + return 0; + } + + if (( sc.is_start == TRUE ) && ( wports_str )) + { + sc.is_start = FALSE; + i_seq_port_wparse( wports_str ); + } + + if ( !i_seq_port_connect() ) + { + i_seq_queue_free(); + i_seq_close(); + sc.seq = NULL; + return 0; + } + + /* success */ + return 1; +} + + +/* shutdown sequencer client */ +gint sequencer_off( void ) +{ + if ( sc.seq ) + { + i_seq_port_disconnect(); + i_seq_queue_free(); + i_seq_close(); + sc.seq = NULL; + /* return 1 here */ + return 1; + } + /* return 2 if it was already freed */ + return 2; +} + + +/* queue set tempo */ +gint sequencer_queue_tempo( gint tempo , gint ppq ) +{ + /* interpret and set tempo */ + snd_seq_queue_tempo_alloca( &sc.queue_tempo ); + snd_seq_queue_tempo_set_tempo( sc.queue_tempo , tempo ); + snd_seq_queue_tempo_set_ppq( sc.queue_tempo , ppq ); + + if ( snd_seq_set_queue_tempo( sc.seq , sc.queue , sc.queue_tempo ) < 0 ) + { + g_warning( "Cannot set queue tempo (%u/%i)\n", + snd_seq_queue_tempo_get_tempo(sc.queue_tempo), + snd_seq_queue_tempo_get_ppq(sc.queue_tempo) ); + return 0; + } + return 1; +} + + +gint sequencer_queue_start( void ) +{ + return snd_seq_start_queue( sc.seq , sc.queue , NULL ); +} + + +gint sequencer_event_init( void ) +{ + /* common settings for all our events */ + snd_seq_ev_clear(&sc.ev); + sc.ev.queue = sc.queue; + sc.ev.source.port = 0; + sc.ev.flags = SND_SEQ_TIME_STAMP_TICK; + return 1; +} + + +gint sequencer_event_noteon( midievent_t * event ) +{ + i_seq_event_common_init( event ); + snd_seq_ev_set_fixed(&sc.ev); + sc.ev.data.note.channel = event->data.d[0]; + sc.ev.data.note.note = event->data.d[1]; + sc.ev.data.note.velocity = event->data.d[2]; + return 1; +} + + +gint sequencer_event_noteoff( midievent_t * event ) +{ + i_seq_event_common_init( event ); + snd_seq_ev_set_fixed(&sc.ev); + sc.ev.data.note.channel = event->data.d[0]; + sc.ev.data.note.note = event->data.d[1]; + sc.ev.data.note.velocity = event->data.d[2]; + return 1; +} + + +gint sequencer_event_keypress( midievent_t * event ) +{ + i_seq_event_common_init( event ); + snd_seq_ev_set_fixed(&sc.ev); + sc.ev.data.note.channel = event->data.d[0]; + sc.ev.data.note.note = event->data.d[1]; + sc.ev.data.note.velocity = event->data.d[2]; + return 1; +} + + +gint sequencer_event_controller( midievent_t * event ) +{ + i_seq_event_common_init( event ); + snd_seq_ev_set_fixed(&sc.ev); + sc.ev.data.control.channel = event->data.d[0]; + sc.ev.data.control.param = event->data.d[1]; + sc.ev.data.control.value = event->data.d[2]; + return 1; +} + + +gint sequencer_event_pgmchange( midievent_t * event ) +{ + i_seq_event_common_init( event ); + snd_seq_ev_set_fixed(&sc.ev); + sc.ev.data.control.channel = event->data.d[0]; + sc.ev.data.control.value = event->data.d[1]; + return 1; +} + + +gint sequencer_event_chanpress( midievent_t * event ) +{ + i_seq_event_common_init( event ); + snd_seq_ev_set_fixed(&sc.ev); + sc.ev.data.control.channel = event->data.d[0]; + sc.ev.data.control.value = event->data.d[1]; + return 1; +} + + +gint sequencer_event_pitchbend( midievent_t * event ) +{ + i_seq_event_common_init( event ); + snd_seq_ev_set_fixed(&sc.ev); + sc.ev.data.control.channel = event->data.d[0]; + sc.ev.data.control.value = ((event->data.d[1]) | ((event->data.d[2]) << 7)) - 0x2000; + return 1; +} + + +gint sequencer_event_sysex( midievent_t * event ) +{ + i_seq_event_common_init( event ); + snd_seq_ev_set_variable(&sc.ev, event->data.length, event->sysex); + return 1; +} + + +gint sequencer_event_tempo( midievent_t * event ) +{ + i_seq_event_common_init( event ); + snd_seq_ev_set_fixed(&sc.ev); + sc.ev.dest.client = SND_SEQ_CLIENT_SYSTEM; + sc.ev.dest.port = SND_SEQ_PORT_SYSTEM_TIMER; + sc.ev.data.queue.queue = sc.queue; + sc.ev.data.queue.param.value = event->data.tempo; + return 1; +} + + +gint sequencer_event_other( midievent_t * event ) +{ + /* unhandled */ + return 1; +} + + +gint sequencer_output( gpointer * buffer , gint * len ) +{ + snd_seq_event_output( sc.seq , &sc.ev ); + snd_seq_drain_output( sc.seq ); + snd_seq_sync_output_queue( sc.seq ); + return 0; +} + + +gint sequencer_output_shut( guint max_tick , gint skip_offset ) +{ + gint i = 0 , c = 0; + /* time to shutdown playback! */ + /* send "ALL SOUNDS OFF" to all channels on all ports */ + sc.ev.type = SND_SEQ_EVENT_CONTROLLER; + sc.ev.time.tick = 0; + snd_seq_ev_set_fixed(&sc.ev); + sc.ev.data.control.param = MIDI_CTL_ALL_SOUNDS_OFF; + sc.ev.data.control.value = 0; + for ( i = 0 ; i < sc.dest_port_num ; i++ ) + { + sc.ev.queue = sc.queue; + sc.ev.dest = sc.dest_port[i]; + + for ( c = 0 ; c < 16 ; c++ ) + { + sc.ev.data.control.channel = c; + snd_seq_event_output(sc.seq, &sc.ev); + snd_seq_drain_output(sc.seq); + } + } + + /* schedule queue stop at end of song */ + snd_seq_ev_clear(&sc.ev); + sc.ev.queue = sc.queue; + sc.ev.source.port = 0; + sc.ev.flags = SND_SEQ_TIME_STAMP_TICK; + + snd_seq_ev_set_fixed(&sc.ev); + sc.ev.type = SND_SEQ_EVENT_STOP; + sc.ev.time.tick = max_tick - skip_offset; + sc.ev.dest.client = SND_SEQ_CLIENT_SYSTEM; + sc.ev.dest.port = SND_SEQ_PORT_SYSTEM_TIMER; + sc.ev.data.queue.queue = sc.queue; + snd_seq_event_output(sc.seq, &sc.ev); + snd_seq_drain_output(sc.seq); + /* snd_seq_sync_output_queue(sc.seq); */ + + return 1; +} + + +gint audio_volume_get( gint * left_volume , gint * right_volume ) +{ + snd_mixer_t * mixer_h = NULL; + snd_mixer_elem_t * mixer_elem = NULL; + gchar mixer_card[10]; + snprintf( mixer_card , 8 , "hw:%i" , amidiplug_cfg_alsa.alsa_mixer_card_id ); + mixer_card[9] = '\0'; + + if ( snd_mixer_open( &mixer_h , 0 ) > -1 ) + i_seq_mixer_find_selem( mixer_h , mixer_card , + amidiplug_cfg_alsa.alsa_mixer_ctl_name , + amidiplug_cfg_alsa.alsa_mixer_ctl_id , + &mixer_elem ); + else + mixer_h = NULL; + + if ( ( mixer_elem ) && ( snd_mixer_selem_has_playback_volume( mixer_elem ) ) ) + { + glong pv_min , pv_max , pv_range; + glong lc, rc; + + snd_mixer_selem_get_playback_volume_range( mixer_elem , &pv_min , &pv_max ); + pv_range = pv_max - pv_min; + if ( pv_range > 0 ) + { + if ( snd_mixer_selem_has_playback_channel( mixer_elem , SND_MIXER_SCHN_FRONT_LEFT ) ) + { + snd_mixer_selem_get_playback_volume( mixer_elem , SND_MIXER_SCHN_FRONT_LEFT , &lc ); + /* convert the range to 0-100 (for the case that pv_range is not 0-100 already) */ + *left_volume = (gint)(((lc - pv_min) * 100) / pv_range); + DEBUGMSG( "GET VOLUME requested, get left channel (%i)\n" , *left_volume ); + } + if ( snd_mixer_selem_has_playback_channel( mixer_elem , SND_MIXER_SCHN_FRONT_RIGHT ) ) + { + snd_mixer_selem_get_playback_volume( mixer_elem , SND_MIXER_SCHN_FRONT_RIGHT , &rc ); + /* convert the range to 0-100 (for the case that pv_range is not 0-100 already) */ + *right_volume = (gint)(((rc - pv_min) * 100) / pv_range); + DEBUGMSG( "GET VOLUME requested, get right channel (%i)\n" , *right_volume ); + } + } + } + + if ( mixer_h ) + snd_mixer_close( mixer_h ); + /* always return 1 here */ + return 1; +} + + +gint audio_volume_set( gint left_volume , gint right_volume ) +{ + snd_mixer_t * mixer_h = NULL; + snd_mixer_elem_t * mixer_elem = NULL; + gchar mixer_card[10]; + snprintf( mixer_card , 8 , "hw:%i" , amidiplug_cfg_alsa.alsa_mixer_card_id ); + mixer_card[9] = '\0'; + + if ( snd_mixer_open( &mixer_h , 0 ) > -1 ) + i_seq_mixer_find_selem( mixer_h , mixer_card , + amidiplug_cfg_alsa.alsa_mixer_ctl_name , + amidiplug_cfg_alsa.alsa_mixer_ctl_id , + &mixer_elem ); + else + mixer_h = NULL; + + if ( ( mixer_elem ) && ( snd_mixer_selem_has_playback_volume( mixer_elem ) ) ) + { + glong pv_min , pv_max , pv_range; + + snd_mixer_selem_get_playback_volume_range( mixer_elem , &pv_min , &pv_max ); + pv_range = pv_max - pv_min; + if ( pv_range > 0 ) + { + if ( snd_mixer_selem_has_playback_channel( mixer_elem , SND_MIXER_SCHN_FRONT_LEFT ) ) + { + DEBUGMSG( "SET VOLUME requested, setting left channel to %i%%\n" , left_volume ); + snd_mixer_selem_set_playback_volume( mixer_elem , SND_MIXER_SCHN_FRONT_LEFT , + (gint)((gdouble)(0.01 * (gdouble)(left_volume * pv_range)) + pv_min) ); + } + if ( snd_mixer_selem_has_playback_channel( mixer_elem , SND_MIXER_SCHN_FRONT_RIGHT ) ) + { + DEBUGMSG( "SET VOLUME requested, setting right channel to %i%%\n" , right_volume ); + snd_mixer_selem_set_playback_volume( mixer_elem , SND_MIXER_SCHN_FRONT_RIGHT , + (gint)((gdouble)(0.01 * (gdouble)(right_volume * pv_range)) + pv_min) ); + } + } + } + + if ( mixer_h ) + snd_mixer_close( mixer_h ); + /* always return 1 here */ + return 1; +} + + +gint audio_info_get( gint * channels , gint * bitdepth , gint * samplerate ) +{ + /* not applicable for ALSA backend */ + *channels = -1; + *bitdepth = -1; + *samplerate = -1; + return 0; /* not valid information */ +} + + +gboolean audio_check_autonomous( void ) +{ + return TRUE; /* ALSA deals directly with audio production */ +} + + + +/* ****************************************************************** + *** EXTRA FUNCTIONS ********************************************** + ****************************************************************** */ + + +/* get a list of writable ALSA MIDI ports + use the data_bucket_t here... + bint[0] = client id , bint[1] = port id + bcharp[0] = client name , bcharp[1] = port name + bpointer[0] = (not used) , bpointer[1] = (not used) */ +GSList * sequencer_port_get_list( void ) +{ + gint err; + snd_seq_t * pseq; + err = snd_seq_open( &pseq , "default" , SND_SEQ_OPEN_DUPLEX , 0 ); + if ( err < 0 ) + return NULL; + + GSList * wports = NULL; + snd_seq_client_info_t *cinfo; + snd_seq_port_info_t *pinfo; + + snd_seq_client_info_alloca( &cinfo ); + snd_seq_port_info_alloca( &pinfo ); + + snd_seq_client_info_set_client( cinfo , -1 ); + while ( snd_seq_query_next_client( pseq , cinfo ) >= 0 ) + { + gint client = snd_seq_client_info_get_client( cinfo ); + snd_seq_port_info_set_client( pinfo , client ); + snd_seq_port_info_set_port( pinfo , -1 ); + while (snd_seq_query_next_port( pseq , pinfo ) >= 0 ) + { + if ((snd_seq_port_info_get_capability(pinfo) + & (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)) + == (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)) + { + data_bucket_t * portinfo = (data_bucket_t*)g_malloc(sizeof(data_bucket_t)); + portinfo->bint[0] = snd_seq_port_info_get_client( pinfo ); + portinfo->bint[1] = snd_seq_port_info_get_port( pinfo ); + portinfo->bcharp[0] = g_strdup(snd_seq_client_info_get_name(cinfo)); + portinfo->bcharp[1] = g_strdup(snd_seq_port_info_get_name(pinfo)); + wports = g_slist_append( wports , portinfo ); + } + } + } + /* snd_seq_port_info_free( pinfo ); + snd_seq_client_info_free( cinfo ); */ + snd_seq_close( pseq ); + return wports; +} + + +void sequencer_port_free_list( GSList * wports ) +{ + GSList * start = wports; + while ( wports != NULL ) + { + data_bucket_t * portinfo = wports->data; + g_free( (gpointer)portinfo->bcharp[0] ); + g_free( (gpointer)portinfo->bcharp[1] ); + g_free( portinfo ); + wports = wports->next; + } + g_slist_free( start ); + return; +} + + +/* get a list of available sound cards and relative mixer controls; + use the data_bucket_t here... + bint[0] = card id , bint[1] = (not used) + bcharp[0] = card name , bcharp[1] = (not used) + bpointer[0] = list (GSList) of mixer controls on the card , bpointer[1] = (not used) */ +GSList * alsa_card_get_list( void ) +{ + gint soundcard_id = -1; + GSList * scards = NULL; + + snd_card_next( &soundcard_id ); + while ( soundcard_id > -1 ) + { + /* card container */ + data_bucket_t * cardinfo = (data_bucket_t*)g_malloc(sizeof(data_bucket_t)); + cardinfo->bint[0] = soundcard_id; + /* snd_card_get_name calls strdup on its own */ + snd_card_get_name( soundcard_id , &cardinfo->bcharp[0] ); + /* for each sound card, get a list of available mixer controls */ + cardinfo->bpointer[0] = i_seq_mixctl_get_list( soundcard_id ); + + scards = g_slist_append( scards , cardinfo ); + snd_card_next( &soundcard_id ); + } + return scards; +} + + +void alsa_card_free_list( GSList * scards ) +{ + GSList * start = scards; + while ( scards != NULL ) + { + data_bucket_t * cardinfo = scards->data; + /* free the list of mixer controls for the sound card */ + i_seq_mixctl_free_list( (GSList*)cardinfo->bpointer[0] ); + g_free( (gpointer)cardinfo->bcharp[0] ); + g_free( cardinfo ); + scards = scards->next; + } + g_slist_free( start ); + return; +} + + + +/* ****************************************************************** + *** INTERNALS **************************************************** + ****************************************************************** */ + + +/* create sequencer client */ +gint i_seq_open( void ) +{ + gint err; + err = snd_seq_open( &sc.seq , "default" , SND_SEQ_OPEN_DUPLEX , 0 ); + if (err < 0) + return 0; + snd_seq_set_client_name( sc.seq , "amidi-plug" ); + return 1; +} + + +/* free sequencer client */ +gint i_seq_close( void ) +{ + if ( snd_seq_close( sc.seq ) < 0 ) + return 0; /* fail */ + else + return 1; /* success */ +} + + +/* create queue */ +gint i_seq_queue_create( void ) +{ + sc.queue = snd_seq_alloc_named_queue( sc.seq , "AMIDI-Plug" ); + if ( sc.queue < 0 ) + return 0; /* fail */ + else + return 1; /* success */ +} + + +/* free queue */ +gint i_seq_queue_free( void ) +{ + if ( snd_seq_free_queue( sc.seq , sc.queue ) < 0 ) + return 0; /* fail */ + else + return 1; /* success */ +} + + +/* create sequencer port */ +gint i_seq_port_create( void ) +{ + sc.client_port = snd_seq_create_simple_port( sc.seq , "AMIDI-Plug" , 0 , + SND_SEQ_PORT_TYPE_MIDI_GENERIC | + SND_SEQ_PORT_TYPE_APPLICATION ); + if ( sc.client_port < 0 ) + return 0; /* fail */ + else + return 1; /* success */ +} + + +/* port connection */ +gint i_seq_port_connect( void ) +{ + gint i = 0 , err = 0; + for ( i = 0 ; i < sc.dest_port_num ; i++ ) + { + if ( snd_seq_connect_to( sc.seq , sc.client_port , + sc.dest_port[i].client , + sc.dest_port[i].port ) < 0 ) + ++err; + } + /* if these values are equal, it means + that all port connections failed */ + if ( err == i ) + return 0; /* fail */ + else + return 1; /* success */ +} + + +/* port disconnection */ +gint i_seq_port_disconnect( void ) +{ + gint i = 0 , err = 0; + for ( i = 0 ; i < sc.dest_port_num ; i++ ) + { + if ( snd_seq_disconnect_to( sc.seq , sc.client_port , + sc.dest_port[i].client , + sc.dest_port[i].port ) < 0 ) + ++err; + } + /* if these values are equal, it means + that all port disconnections failed */ + if ( err == i ) + return 0; /* fail */ + else + return 1; /* success */ +} + + +/* parse writable ports */ +gint i_seq_port_wparse( gchar * wportlist ) +{ + gint i = 0 , err = 0; + gchar **portstr = g_strsplit( wportlist , "," , 0 ); + + sc.dest_port_num = 0; + + /* fill sc.dest_port_num with the writable port number */ + while ( portstr[sc.dest_port_num] != NULL ) + ++sc.dest_port_num; + + /* check if there is already an allocated array and free it */ + if ( sc.dest_port ) + free( sc.dest_port ); + + if ( sc.dest_port_num > 0 ) + /* allocate the array of writable ports */ + sc.dest_port = calloc( sc.dest_port_num , sizeof(snd_seq_addr_t) ); + + for ( i = 0 ; i < sc.dest_port_num ; i++ ) + { + if ( snd_seq_parse_address( sc.seq , &sc.dest_port[i] , portstr[i] ) < 0 ) + ++err; + } + + g_strfreev( portstr ); + + /* if these values are equal, it means + that all port translations failed */ + if ( err == i ) + return 0; /* fail */ + else + return 1; /* success */ +} + + +gint i_seq_event_common_init( midievent_t * event ) +{ + sc.ev.type = event->type; + sc.ev.time.tick = event->tick_real; + sc.ev.dest = sc.dest_port[event->port]; + return 1; +} + + +/* get a list of available mixer controls for a given sound card; + use the data_bucket_t here... + bint[0] = control id , bint[1] = (not used) + bcharp[0] = control name , bcharp[1] = (not used) + bpointer[0] = (not used) , bpointer[1] = (not used) */ +GSList * i_seq_mixctl_get_list( gint soundcard_id ) +{ + GSList * mixctls = NULL; + snd_mixer_t * mixer_h; + snd_mixer_selem_id_t * mixer_selem_id; + snd_mixer_elem_t * mixer_elem; + gchar card[10]; + + snprintf( card , 8 , "hw:%i" , soundcard_id ); + card[9] = '\0'; + + snd_mixer_selem_id_alloca( &mixer_selem_id ); + snd_mixer_open( &mixer_h , 0 ); + snd_mixer_attach( mixer_h , card ); + snd_mixer_selem_register( mixer_h , NULL , NULL ); + snd_mixer_load( mixer_h ); + for ( mixer_elem = snd_mixer_first_elem( mixer_h ) ; mixer_elem ; + mixer_elem = snd_mixer_elem_next( mixer_elem ) ) + { + data_bucket_t * mixctlinfo = (data_bucket_t*)g_malloc(sizeof(data_bucket_t)); + snd_mixer_selem_get_id( mixer_elem , mixer_selem_id ); + mixctlinfo->bint[0] = snd_mixer_selem_id_get_index(mixer_selem_id); + mixctlinfo->bcharp[0] = g_strdup(snd_mixer_selem_id_get_name(mixer_selem_id)); + mixctls = g_slist_append( mixctls , mixctlinfo ); + } + snd_mixer_close( mixer_h ); + return mixctls; +} + + +void i_seq_mixctl_free_list( GSList * mixctls ) +{ + GSList * start = mixctls; + while ( mixctls != NULL ) + { + data_bucket_t * mixctlinfo = mixctls->data; + g_free( (gpointer)mixctlinfo->bcharp[0] ); + g_free( mixctlinfo ); + mixctls = mixctls->next; + } + g_slist_free( start ); + return; +} + + +gint i_seq_mixer_find_selem( snd_mixer_t * mixer_h , gchar * mixer_card , + gchar * mixer_control_name , gint mixer_control_id , + snd_mixer_elem_t ** mixer_elem ) +{ + snd_mixer_selem_id_t * mixer_selem_id = NULL; + snd_mixer_selem_id_alloca( &mixer_selem_id ); + snd_mixer_selem_id_set_index( mixer_selem_id , mixer_control_id ); + snd_mixer_selem_id_set_name( mixer_selem_id , mixer_control_name ); + snd_mixer_attach( mixer_h , mixer_card ); + snd_mixer_selem_register( mixer_h , NULL , NULL); + snd_mixer_load( mixer_h ); + /* assign the mixer element (can be NULL if there is no such element) */ + *mixer_elem = snd_mixer_find_selem( mixer_h , mixer_selem_id ); + /* always return 1 here */ + return 1; +} + + +gchar * i_configure_read_seq_ports_default( void ) +{ + FILE * fp = NULL; + /* first try, get seq ports from proc on card0 */ + fp = fopen( "/proc/asound/card0/wavetableD1" , "rb" ); + if ( fp ) + { + gchar buffer[100]; + while ( !feof( fp ) ) + { + fgets( buffer , 100 , fp ); + if (( strlen( buffer ) > 11 ) && ( !strncasecmp( buffer , "addresses: " , 11 ) )) + { + /* change spaces between ports (65:0 65:1 65:2 ...) + into commas (65:0,65:1,65:2,...) */ + g_strdelimit( &buffer[11] , " " , ',' ); + /* remove lf and cr from the end of the string */ + g_strdelimit( &buffer[11] , "\r\n" , '\0' ); + /* ready to go */ + DEBUGMSG( "init, default values for seq ports detected: %s\n" , &buffer[11] ); + fclose( fp ); + return g_strdup( &buffer[11] ); + } + } + fclose( fp ); + } + + /* second option: do not set ports at all, let the user + select the right ones in the nice config window :) */ + return g_strdup( "" ); +} + + +/* count the number of occurrencies of a specific character 'c' + in the string 'string' (it must be a null-terminated string) */ +gint i_util_str_count( gchar * string , gchar c ) +{ + gint i = 0 , count = 0; + while ( string[i] != '\0' ) + { + if ( string[i] == c ) + ++count; + ++i; + } + return count; +} + + +void i_cfg_read( void ) +{ + pcfg_t *cfgfile; + gchar * config_pathfilename = g_strjoin( "" , g_get_home_dir() , "/" , + PLAYER_LOCALRCDIR , "/amidi-plug.conf" , NULL ); + cfgfile = i_pcfg_new_from_file( config_pathfilename ); + + if ( !cfgfile ) + { + /* alsa backend defaults */ + amidiplug_cfg_alsa.alsa_seq_wports = i_configure_read_seq_ports_default(); + amidiplug_cfg_alsa.alsa_mixer_card_id = 0; + amidiplug_cfg_alsa.alsa_mixer_ctl_name = g_strdup( "Synth" ); + amidiplug_cfg_alsa.alsa_mixer_ctl_id = 0; + } + else + { + i_pcfg_read_string( cfgfile , "alsa" , "alsa_seq_wports" , + &amidiplug_cfg_alsa.alsa_seq_wports , NULL ); + if ( amidiplug_cfg_alsa.alsa_seq_wports == NULL ) + amidiplug_cfg_alsa.alsa_seq_wports = i_configure_read_seq_ports_default(); /* pick default values */ + + i_pcfg_read_integer( cfgfile , "alsa" , "alsa_mixer_card_id" , + &amidiplug_cfg_alsa.alsa_mixer_card_id , 0 ); + + i_pcfg_read_string( cfgfile , "alsa" , "alsa_mixer_ctl_name" , + &amidiplug_cfg_alsa.alsa_mixer_ctl_name , "Synth" ); + + i_pcfg_read_integer( cfgfile , "alsa" , "alsa_mixer_ctl_id" , + &amidiplug_cfg_alsa.alsa_mixer_ctl_id , 0 ); + + i_pcfg_free( cfgfile ); + } + + g_free( config_pathfilename ); +} + + +void i_cfg_free( void ) +{ + g_free( amidiplug_cfg_alsa.alsa_seq_wports ); + g_free( amidiplug_cfg_alsa.alsa_mixer_ctl_name ); +}