📄 seq_alsa.c
字号:
/*================================================================== * seq_alsa.c - ALSA sequencer and virtual keyboard routines * * Smurf Sound Font Editor * Copyright (C) 1999-2001 Josh Green * * Some code and ideas used from the following: * aconnect (in alsa-utils package) and vkeybd * both programs are Copyright Takashi Iwai * * 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., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA or point your web browser to http://www.gnu.org. * * To contact the author of this program: * Email: Josh Green <jgreen@users.sourceforge.net> * Smurf homepage: http://smurf.sourceforge.net *==================================================================*/#include "config.h"/* only compile if ALSA_SUPPORT */#ifdef ALSA_SUPPORT#include <stdio.h>#include <unistd.h>#include <fcntl.h>#include <sys/ioctl.h>#include <sys/poll.h>#include <glib.h>#include <string.h>#include "alsa.h"#include "seq_alsa.h"#include "i18n.h"#include "smurfcfg.h"#include "util.h"#include "wavetable.h"#include <sys/asoundlib.h>#ifdef NEW_ALSA_SEQ#define DEFAULT_SEQ_DEV "default"#else#define DEFAULT_SEQ_DEV "hw"#endifstatic void seq_alsa_send_event(int do_flush);snd_seq_t *seq_alsa_handle = NULL;static gint seq_alsa_usage_count = 0;gint seq_alsa_fd = -1;gint seq_alsa_vkeyb_conn_client = 0;gint seq_alsa_vkeyb_conn_port = 0;gboolean seq_alsa_vkeyb_active = FALSE;static gint seq_alsa_vkeyb_port; /* virtual keyboard port (piano widget) */static snd_seq_event_t ev;/* load ALSA config vars (sequencer client:port to connect to) */voidseq_alsa_load_config (void){ if (strcmp (smurfcfg_get_val (SMURFCFG_SEQ_DRIVER)->v_string, "AUTO") != 0) { seq_alsa_vkeyb_conn_client = smurfcfg_get_val (SMURFCFG_SEQ_ALSACLIENT)->v_int; seq_alsa_vkeyb_conn_port = smurfcfg_get_val (SMURFCFG_SEQ_ALSAPORT)->v_int; } else seq_alsa_vkeyb_conn_client = 0; /* force "AUTO" detection */}/* open ALSA handle (used by ALSA MIDI thru drivers, and wavetable driver when ALSA has support for patch loading) */gintseq_alsa_init (void){ int err;#ifdef NEW_ALSA struct pollfd pfd;#endif if (seq_alsa_usage_count) { seq_alsa_usage_count++; return (OK); }#ifdef NEW_ALSA if ((err = snd_seq_open (&seq_alsa_handle, DEFAULT_SEQ_DEV, SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK)) < 0) return (logit (LogFubar, _("Failed to open ALSA sequencer: %s"), snd_strerror (err))); if ((err = snd_seq_nonblock (seq_alsa_handle, FALSE)) < 0) { snd_seq_close (seq_alsa_handle); return (logit (LogFubar | LogErrno, _("Failed to set ALSA sequencer to non-blocking"))); } /* grab one file descriptor POLL info structure */ err = snd_seq_poll_descriptors (seq_alsa_handle, &pfd, 1, POLLIN); if (err <= 0 || !(pfd.events & POLLIN)) { snd_seq_close (seq_alsa_handle); return (logit (LogFubar, _("Failed to get ALSA sequencer file descriptor: %s"), snd_strerror (err))); } seq_alsa_fd = pfd.fd; /* indicate sample caching support, ALSA 0.9.0beta3 works with sample caching older versions <= 0.5.10b apparently don't (strange bahavior including kernel oopses) */ wtbl_sample_cache_support = TRUE;#else /* hmm, for some reason SND_SEQ_FILTER_BOUNCE doesn't work! */ if ((err = snd_seq_open (&seq_alsa_handle, SND_SEQ_OPEN_OUT)) < 0) return (logit (LogFubar | LogErrno, _("Failed to open ALSA sequencer: %s"), snd_strerror (err))); if (snd_seq_block_mode (seq_alsa_handle, FALSE) < 0) return (logit (LogFubar | LogErrno, _("Failed to set ALSA sequencer to non-blocking"))); /* get the file descriptor for the opened sequencer handle */ if ((seq_alsa_fd = snd_seq_file_descriptor (seq_alsa_handle)) < 0) { snd_seq_close (seq_alsa_handle); return (logit (LogFubar, _("Failed to get ALSA sequencer file descriptor: %s"), snd_strerror (seq_alsa_fd))); }#endif snd_seq_set_client_name (seq_alsa_handle, _("Smurf Sound Font Editor")); snd_seq_ev_clear (&ev); seq_alsa_usage_count++; return (OK);}voidseq_alsa_close (void){ if (!seq_alsa_usage_count) return; /* ALSA handle opened? */ if (!--seq_alsa_usage_count) /* no more drivers using ALSA handle? */ snd_seq_close (seq_alsa_handle); /* close ALSA handle */}gintseq_alsa_vkeyb_init (void){ gint client, port; gint perm, type; if (!seq_alsa_init ()) return (FAIL); if ((seq_alsa_vkeyb_port = snd_seq_create_simple_port (seq_alsa_handle, _("Smurf Virtual Keyboard"), SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ, SND_SEQ_PORT_TYPE_APPLICATION)) < 0) { seq_alsa_close (); return (logit (LogFubar | LogErrno, _("Failed to create ALSA virtual keyboard port"))); } seq_alsa_vkeyb_active = TRUE; client = seq_alsa_vkeyb_conn_client; port = seq_alsa_vkeyb_conn_port; if (client == 0) /* "auto" detect WaveTable to sequence? */ { log_message (_("Looking for ALSA WaveTable synth to sequence")); perm = SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE; type = SND_SEQ_PORT_TYPE_MIDI_GENERIC; /* probably not the best "auto" detection scheme, dependance on client name containing "WaveTable" is probably the first mistake, but user can set client:port manually in preferences */ if (!seq_alsa_find_port ("WaveTable", perm, type, &client, &port)) { /* wavetable not found? then allow user to manually connect it */ logit (LogWarn, _("Failed to find suitable synth, set manually in" " preferences or use ALSA aconnect utility")); return (OK); } } log_message (_("Connecting Virtual Keyboard to ALSA client:port %d:%d"), client, port); if (snd_seq_connect_to (seq_alsa_handle,seq_alsa_vkeyb_port,client,port) < 0) log_message (_("Failed to connect ALSA Virtual Keyboard to %d:%d"), client, port); return (OK);}voidseq_alsa_vkeyb_close (void){ if (!seq_alsa_vkeyb_active) return; if (snd_seq_delete_simple_port (seq_alsa_handle, seq_alsa_vkeyb_port) < 0) logit (LogWarn, _("Failed to close Virtual Keyboard port")); seq_alsa_close (); seq_alsa_vkeyb_active = FALSE;}/* searches for an ALSA client whose client name matches the substring "namematch" and the first port with permission "perm" and type "type" */#ifdef NEW_ALSA_SEQ /* ALSA >= 0.9.0beta6 interface */gintseq_alsa_find_port (gchar *namematch, gint perm, gint type, gint *client, gint *port){ snd_seq_client_info_t *cinfo; snd_seq_port_info_t *pinfo; const gchar *getname; 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 (seq_alsa_handle, cinfo) >= 0) { getname = snd_seq_client_info_get_name (cinfo); if (!namematch || strstr (getname, namematch)) { snd_seq_port_info_set_client (pinfo, snd_seq_client_info_get_client (cinfo)); snd_seq_port_info_set_port (pinfo, -1); while (snd_seq_query_next_port (seq_alsa_handle, pinfo) >= 0) { if ((snd_seq_port_info_get_capability (pinfo) & perm) == perm && (snd_seq_port_info_get_type (pinfo) & type) == type) { *client = snd_seq_port_info_get_client (pinfo); *port = snd_seq_port_info_get_port (pinfo); return (OK); } } } } return (FAIL);}#else /* ALSA 0.9.0beta? - 0.9.0beta5 */gintseq_alsa_find_port (gchar *namematch, gint perm, gint type, gint *client, gint *port){ snd_seq_client_info_t cinfo; snd_seq_port_info_t pinfo; cinfo.client = -1; cinfo.name[0] = 0; cinfo.group[0] = 0; while (snd_seq_query_next_client (seq_alsa_handle, &cinfo) >= 0) { if (!namematch || strstr (cinfo.name, namematch)) { pinfo.client = cinfo.client; pinfo.port = -1; pinfo.name[0] = 0; pinfo.group[0] = 0; while (snd_seq_query_next_port (seq_alsa_handle, &pinfo) >= 0) { if ((pinfo.capability & perm) == perm && (pinfo.type & type) == type) { *client = cinfo.client; *port = pinfo.port; return (OK); } pinfo.name[0] = 0; pinfo.group[0] = 0; } } cinfo.name[0] = 0; cinfo.group[0] = 0; } return (FAIL);}#endif/* check if two ALSA client/port pairs are subscribed */#ifdef NEW_ALSA_SEQ /* ALSA >= 0.9.0beta6 interface */gbooleanseq_alsa_is_subscribed (snd_seq_addr_t *src, snd_seq_addr_t *dest){ snd_seq_query_subscribe_t *query; snd_seq_query_subscribe_alloca (&query); snd_seq_query_subscribe_set_type (query, SND_SEQ_QUERY_SUBS_READ); snd_seq_query_subscribe_set_index(query, 0); while (snd_seq_query_port_subscribers(seq_alsa_handle, query) >= 0) { const snd_seq_addr_t *addr; addr = snd_seq_query_subscribe_get_addr(query); if (addr->client == dest->client && addr->port == dest->port) return (TRUE); snd_seq_query_subscribe_set_index (query, snd_seq_query_subscribe_get_index (query) + 1); } return (FALSE);}#else /* ALSA 0.9.0beta? - 0.9.0beta5 or 0.5.x */gbooleanseq_alsa_is_subscribed (snd_seq_addr_t *src, snd_seq_addr_t *dest){ snd_seq_query_subs_t query; memset (&query, sizeof (query), 0); query.type = SND_SEQ_QUERY_SUBS_READ; while (snd_seq_query_port_subscribers(seq_alsa_handle, &query) >= 0) { if (query.addr.client == dest->client && query.addr.port == dest->port) return (TRUE); query.index++; } return (FALSE);}#endifstatic voidseq_alsa_send_event(int do_flush){ snd_seq_ev_set_subs (&ev); snd_seq_ev_set_direct(&ev); snd_seq_ev_set_source(&ev, seq_alsa_vkeyb_port); snd_seq_event_output(seq_alsa_handle, &ev);#ifdef NEW_ALSA if (do_flush) snd_seq_drain_output (seq_alsa_handle);#else if (do_flush) snd_seq_flush_output (seq_alsa_handle);#endif}/* set patch bank for specified channel */voidseq_alsa_set_bank (gint chan, gint bank){#ifdef NEW_ALSA snd_seq_ev_set_controller (&ev, chan, MIDI_CTL_MSB_BANK, bank);#else snd_seq_ev_set_controller (&ev, chan, SND_MCTL_MSB_BANK, bank);#endif seq_alsa_send_event (TRUE);}/* set patch preset for specified channel */voidseq_alsa_set_preset (gint chan, gint preset){ snd_seq_ev_set_pgmchange (&ev, chan, preset); seq_alsa_send_event (TRUE);}/* Start note (note) on channel (chan) with velocity (vel) */voidseq_alsa_note_on (gint chan, gint note, gint vel){ snd_seq_ev_set_noteon (&ev, chan, note, vel); seq_alsa_send_event (TRUE);}/* Stop note (note) on channel (chan) with velocity (vel) */voidseq_alsa_note_off (gint chan, gint note, gint vel){ snd_seq_ev_set_noteoff (&ev, chan, note, vel); seq_alsa_send_event (TRUE);}/* Set midi bender control on channel (chan) to (bendval) */voidseq_alsa_pitch_bender (gint chan, gint bendval){ snd_seq_ev_set_pitchbend (&ev, chan, bendval); seq_alsa_send_event (TRUE);}/* set bend range */voidseq_alsa_pitch_bend_range (gint chan, gint val){ ev.type = SND_SEQ_EVENT_REGPARAM; ev.data.control.channel = chan; ev.data.control.param = 0; /* RPN type 0 is bend range */ ev.data.control.value = val << 7; /* value is stored in MSB of RPN value */ seq_alsa_send_event (TRUE);}/* set main volume */voidseq_alsa_main_volume (gint chan, gint val){#ifdef NEW_ALSA snd_seq_ev_set_controller (&ev, chan, MIDI_CTL_MSB_MAIN_VOLUME, val);#else snd_seq_ev_set_controller (&ev, chan, SND_MCTL_MSB_MAIN_VOLUME, val);#endif seq_alsa_send_event (TRUE);}/* Set chorus amount */voidseq_alsa_chorus (gint chan, gint val){#ifdef NEW_ALSA snd_seq_ev_set_controller (&ev, chan, MIDI_CTL_E3_CHORUS_DEPTH, val);#else snd_seq_ev_set_controller (&ev, chan, SND_MCTL_E3_CHORUS_DEPTH, val);#endif seq_alsa_send_event (TRUE);}/* Set reverb amount */voidseq_alsa_reverb (gint chan, gint val){#ifdef NEW_ALSA snd_seq_ev_set_controller (&ev, chan, MIDI_CTL_E1_REVERB_DEPTH, val);#else snd_seq_ev_set_controller (&ev, chan, SND_MCTL_E1_REVERB_DEPTH, val);#endif seq_alsa_send_event (TRUE);}#endif /* #ifdef ALSA_SUPPORT */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -