Mercurial > audlegacy-plugins
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 } |