From e978fc745e612c57d65402da5e7d33a2b79e700f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 30 Oct 2007 11:53:40 +0100 Subject: [PATCH] sbiload - Rewritten to use hwdep device Major rewritten to use hwdep device instead of instrument layer. Also, more options (-c, -2, -q, -D) and better guess work for patch types now. --- seq/sbiload/README | 76 +++-- seq/sbiload/configure.in | 4 +- seq/sbiload/sbiload.c | 692 +++++++++++++++------------------------ 3 files changed, 304 insertions(+), 468 deletions(-) diff --git a/seq/sbiload/README b/seq/sbiload/README index 4014c80..914f026 100644 --- a/seq/sbiload/README +++ b/seq/sbiload/README @@ -1,4 +1,4 @@ -This is sbiload - an OPL2/3 FM instrument loader for ALSA sequencer +This is sbiload - an OPL2/3 FM instrument loader for ALSA hwdep Written by Uros Bizjak Web: http://www.kss-loka.si/~uros/sbiload.html @@ -14,63 +14,61 @@ are in make.conf if you need to change anything. By default it will install into /usr/local/bin, change INSTDIR in make.conf to change this. -Before use ----------- - -Please check that the correct sequencer modules are loaded for your -sound card. You need to load snd-synth-opl3 module. - Usage ----- - loadsbi [-p client:port] [-4] [-l] [-P path] [-v level] instfile drumfile + sbiload [options] [instfile [drumfile]] + sbiload [options] -c - -p, --port=client:port - An ALSA client and port number to use + -D, --device=name - An ALSA hwdep name to use + -c, --clear - Clear patches and exit + -2 --opl2 - Two operators (OPL2) file type -4 --opl3 - Four operators (OPL3) file type - -l, --list - List possible output ports -P, --path=path - Specify the patch path -v, --verbose=level - Verbose level + -q, --quiet - Be quiet, no error/warning messages + -V, --version - Show version -Because you may have more than one sound card and each sound -card may have several MIDI connections, you have to tell sbiload -which one to use. - -First find out what the possibilities are for your system: +When you have multiple sound cards and/or there may be multiple OPL2/3 +synth devices, you have to tell sbiload which one to use, and pass its +hwdep name via -D option. Otherwise, sbiload will load the data onto +the first found device. -If you run: - - sbiload -l +To find out the hwdep name of the OPL devices, check +/proc/asound/hwdep file first. It has the list of hwdep devices +currently available on your system. -it will give you a list of the devices. On my system I get: - - Port Client name Port name - 64:0 0: MIDI Synth MIDI 0-0 - 65:0 OPL3 FM Synth OPL3 FM Synth port + % cat /proc/asound/hwdep + 00-00: Emux WaveTable + 00-01: OPL3 FM + 01-00: OPL3 FM +In the example above, you have two OPL3 devices. To the first one +(00-01), which means the first card (00-) and the second device (01), +pass "-D hw:0,1". For the second OPL3 device (01-00), pass "-D +hw:1,0" instead. -Only OPLx FM Synth port can be used with sbiload. In the example above -the first one is the external Midi port, and the second is the internal -four-operator FM synthesizer (OPL3). - -If you don't see anything listed then check that the correct ALSA -modules are loaded as in the section "Before use". +For convenience, -D option accepts a string like /dev/snd/hwC0D3. +This is automatically parsed as "hw:0,3". Running sbiload --------------- -You specify the port to use with the -p option, or by setting the -environment variable ALSA_OUT_PORT. +Pass the instrument and drum file data as arguments. + + % sbiload std.o3 drums.o3 -For example to use port 0 on client 65 to load four-operator FM -instrument - - sbiload -p 65:0 --opl3 std.o3 drums.o3 +Without a file type option (-2 or -4), sbiload will guess the file +type according to the hwdep iface type. If it's not correct, pass -2 +or -4 option explicitly. +Also, when the given file isn't found, sbiload tries to load a file +with an appropriate extension, .sb for OPL2 and .o3 for OPL3. -or - - export ALSA_OUT_PORT=65:0 - sbiload --opl3 std.o3 drums.o3 +You can omit the instrument and the drum file names, too. In this +case, the default file name "std" and "drums" are chosen. +The option -c is to mean just to clear patches and exit without +loading patches. Acknowledgements ---------------- diff --git a/seq/sbiload/configure.in b/seq/sbiload/configure.in index a00dd6c..fd01843 100644 --- a/seq/sbiload/configure.in +++ b/seq/sbiload/configure.in @@ -1,9 +1,9 @@ AC_INIT(sbiload.c) -AM_INIT_AUTOMAKE(sbiload, 0.3.0) +AM_INIT_AUTOMAKE(sbiload, 0.4.0) AC_PROG_CC AC_PROG_INSTALL AC_HEADER_STDC -AM_PATH_ALSA(0.9.0) +AM_PATH_ALSA(1.0.15) CFLAGS="$CFLAGS $ALSA_CFLAGS" LDFLAGS="$LDFLAGS $ALSA_LIBS" diff --git a/seq/sbiload/sbiload.c b/seq/sbiload/sbiload.c index 93e12bf..7465a82 100644 --- a/seq/sbiload/sbiload.c +++ b/seq/sbiload/sbiload.c @@ -1,5 +1,5 @@ /* - * ALSA sequencer SBI FM instrument loader + * ALSA hwdep SBI FM instrument loader * Copyright (c) 2000 Uros Bizjak * * This program is free software; you can redistribute it and/or modify @@ -16,11 +16,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * + * + * Oct. 2007 - Takashi Iwai + * Changed to use hwdep instead of obsoleted seq-instr interface */ #include #include -#include #include #include #include @@ -29,24 +31,13 @@ #include #include #include +#include #include +#include #include -typedef struct sbi_header -{ - char key[4]; - char name[32]; -} -sbi_header_t; - -typedef struct sbi_inst -{ - sbi_header_t header; #define DATA_LEN_2OP 16 #define DATA_LEN_4OP 24 - char data[DATA_LEN_4OP]; -} -sbi_inst_t; /* offsets for SBI params */ #define AM_VIB 0 @@ -59,23 +50,25 @@ sbi_inst_t; #define CONNECTION 10 #define OFFSET_4OP 11 -/* offsets in sbi_header.name for SBI extensions */ -#define ECHO_DELAY 25 -#define ECHO_ATTEN 26 -#define CHORUS_SPREAD 27 -#define TRNSPS 28 -#define FIX_DUR 29 -#define MODES 30 -#define FIX_KEY 31 +/* offsets for SBI extensions */ +#define ECHO_DELAY 0 +#define ECHO_ATTEN 1 +#define CHORUS_SPREAD 2 +#define TRNSPS 3 +#define FIX_DUR 4 +#define MODES 5 +#define FIX_KEY 6 /* Options for the command */ #define HAS_ARG 1 static struct option long_opts[] = { - {"port", HAS_ARG, NULL, 'p'}, + {"device", HAS_ARG, NULL, 'D'}, + {"opl2", 0, NULL, '2'}, {"opl3", 0, NULL, '4'}, - {"list", 0, NULL, 'l'}, + {"clear", 0, NULL, 'c'}, {"path", HAS_ARG, NULL, 'P'}, {"verbose", HAS_ARG, NULL, 'v'}, + {"quiet", 0, NULL, 'q'}, {"version", 0, NULL, 'V'}, {0, 0, 0, 0}, }; @@ -83,87 +76,37 @@ static struct option long_opts[] = { /* Number of elements in an array */ #define NELEM(a) ( sizeof(a)/sizeof((a)[0]) ) -#define ADDR_PARTS 4 /* Number of part in a port description addr 1:2:3:4 */ -#define SEP ", \t" /* Separators for port description */ - -#define SBI_FILE_TYPE_2OP 0 -#define SBI_FILE_TYPE_4OP 1 +enum { + FM_PATCH_UNKNOWN, + FM_PATCH_OPL2, + FM_PATCH_OPL3 +}; /* Default file type */ -int file_type = SBI_FILE_TYPE_2OP; +static int file_type = FM_PATCH_UNKNOWN; /* Default verbose level */ -int verbose = 0; +static int quiet; +static int verbose = 0; /* Global declarations */ -snd_seq_t *seq_handle; - -int seq_client; -int seq_port; -int seq_dest_client; -int seq_dest_port; +static snd_hwdep_t *handle; +static int iface; #ifndef PATCHDIR #define PATCHDIR "/usr/share/sounds/opl3" #endif -char *patchdir = PATCHDIR; +static char *patchdir = PATCHDIR; /* Function prototypes */ -static void show_list (); -static void show_usage (); -static void show_op (fm_instrument_t * fm_instr); +static void show_usage (void); +static void show_op (struct sbi_patch * instr, int type); -static int load_patch (fm_instrument_t * fm_instr, int bank, int prg, char *name); +static int load_patch (struct sbi_patch * instr); static int load_file (int bank, char *filename); -static int parse_portdesc (char *portdesc); -static int init_client (); - -static void -ignore_errors (const char *file, int line, const char *function, int err, const char *fmt, ...) -{ - /* ignore */ -} - -/* - * Show a list of possible output ports that midi could be sent to. - */ -static void -show_list () { - snd_seq_client_info_t *cinfo; - snd_seq_port_info_t *pinfo; - - int client, err; - - snd_lib_error_set_handler (ignore_errors); - if ((err = snd_seq_open (&seq_handle, "hw", SND_SEQ_OPEN_DUPLEX, 0)) < 0) { - fprintf (stderr, "Could not open sequencer: %s\n", snd_strerror (err)); - return; - } - - printf (" Port %-30.30s %s\n", "Client name", "Port name"); - snd_seq_client_info_alloca(&cinfo); - snd_seq_client_info_set_client(cinfo, -1); - while (snd_seq_query_next_client(seq_handle, cinfo) >= 0) { - client = snd_seq_client_info_get_client(cinfo); - snd_seq_port_info_alloca(&pinfo); - snd_seq_port_info_set_client(pinfo, client); - snd_seq_port_info_set_port(pinfo, -1); - while (snd_seq_query_next_port(seq_handle, pinfo) >= 0) { - unsigned int cap; - - cap = (SND_SEQ_PORT_CAP_SUBS_WRITE | SND_SEQ_PORT_CAP_WRITE); - if ((snd_seq_port_info_get_capability(pinfo) & cap) == cap) { - printf ("%3d:%-3d %-30.30s %s\n", - client, snd_seq_port_info_get_port(pinfo), - snd_seq_client_info_get_name(cinfo), - snd_seq_port_info_get_name(pinfo)); - } - } - } - snd_seq_close (seq_handle); -} +static int init_hwdep (const char *name); /* * Show usage message @@ -172,18 +115,22 @@ static void show_usage () { char **cpp; static char *msg[] = { - "Usage: sbiload [-p client:port] [-4] [-l] [-P path] [-v level] instfile drumfile", + "Usage: sbiload [options] [instfile [drumfile]]", + " sbiload [options] -c", "", - " -p client:port - A alsa client and port number to send midi to", - " -4 - four operators file type (default = two ops)", - " -l - List possible output ports that could be used", - " -P path - Specify the patch path", - " -v level - Verbose level (default = 0)", - " -V - Show version", + " -D, --device=name - hwdep device string", + " -c, --clear - Clear patches and exit", + " -2, --opl2 - two operators file type (OPL2)", + " -4, --opl3 - four operators file type (OPL3)", + " -P, --path=path - Specify the patch path", + " (default path: " PATCHDIR ")", + " -v, --verbose=level - Verbose level (default = 0)", + " -q, --quiet - Be quiet, no error/warning messages", + " -V, --version - Show version", }; for (cpp = msg; cpp < msg + NELEM (msg); cpp++) { - fprintf (stderr, "%s\n", *cpp); + printf ("%s\n", *cpp); } } @@ -199,169 +146,87 @@ show_version () { * Show instrument FM operators */ static void -show_op (fm_instrument_t * fm_instr) { +show_op (struct sbi_patch * inst, int type) { int i = 0; + int ofs = 0; do { - printf (" OP%i: flags: %s %s %s %s\011OP%i: flags: %s %s %s %s\n", - i, - fm_instr->op[i].am_vib & (1 << 7) ? "AM" : " ", - fm_instr->op[i].am_vib & (1 << 6) ? "VIB" : " ", - fm_instr->op[i].am_vib & (1 << 5) ? "EGT" : " ", - fm_instr->op[i].am_vib & (1 << 4) ? "KSR" : " ", - i + 1, - fm_instr->op[i + 1].am_vib & (1 << 7) ? "AM" : " ", - fm_instr->op[i + 1].am_vib & (1 << 6) ? "VIB" : " ", - fm_instr->op[i + 1].am_vib & (1 << 5) ? "EGT" : " ", - fm_instr->op[i + 1].am_vib & (1 << 4) ? "KSR" : ""); - printf (" OP%i: MULT = 0x%x" "\011\011OP%i: MULT = 0x%x\n", - i, fm_instr->op[i].am_vib & 0x0f, - i + 1, fm_instr->op[i + 1].am_vib & 0x0f); - printf (" OP%i: KSL = 0x%x TL = 0x%.2x\011OP%i: KSL = 0x%x TL = 0x%.2x\n", - i, (fm_instr->op[i].ksl_level >> 6) & 0x03, fm_instr->op[i].ksl_level & 0x3f, - i + 1, (fm_instr->op[i + 1].ksl_level >> 6) & 0x03, fm_instr->op[i + 1].ksl_level & 0x3f); - printf (" OP%i: AR = 0x%x DL = 0x%x\011OP%i: AR = 0x%x DL = 0x%x\n", - i, (fm_instr->op[i].attack_decay >> 4) & 0x0f, fm_instr->op[i].attack_decay & 0x0f, - i + 1, (fm_instr->op[i + 1].attack_decay >> 4) & 0x0f, fm_instr->op[i + 1].attack_decay & 0x0f); - printf (" OP%i: SL = 0x%x RR = 0x%x\011OP%i: SL = 0x%x RR = 0x%x\n", - i, (fm_instr->op[i].sustain_release >> 4) & 0x0f, fm_instr->op[i].sustain_release & 0x0f, - i + 1, (fm_instr->op[i + 1].sustain_release >> 4) & 0x0f, fm_instr->op[i + 1].sustain_release & 0x0f); - printf (" OP%i: WS = 0x%x\011\011OP%i: WS = 0x%x\n", - i, fm_instr->op[i].wave_select & 0x07, - i + 1, fm_instr->op[i + 1].wave_select & 0x07); - printf (" FB = 0x%x, %s\n", - (fm_instr->feedback_connection[i / 2] >> 1) & 0x07, - fm_instr->feedback_connection[i / 2] & (1 << 0) ? "parallel" : "serial"); - i += 2; + unsigned char val; + val = inst->data[AM_VIB + ofs]; + printf (" OP%i: flags: %s %s %s %s", i, + val & (1 << 7) ? "AM" : " ", + val & (1 << 6) ? "VIB" : " ", + val & (1 << 5) ? "EGT" : " ", + val & (1 << 4) ? "KSR" : " "); + val = inst->data[AM_VIB + ofs + 1]; + printf ("\011OP%i: flags: %s %s %s %s\n", i + 1, + val & (1 << 7) ? "AM" : " ", + val & (1 << 6) ? "VIB" : " ", + val & (1 << 5) ? "EGT" : " ", + val & (1 << 4) ? "KSR" : ""); + val = inst->data[AM_VIB + ofs]; + printf (" OP%i: MULT = 0x%x", i, val & 0x0f); + val = inst->data[AM_VIB + ofs + 1]; + printf ("\011\011OP%i: MULT = 0x%x\n", i + 1, val & 0x0f); + + val = inst->data[KSL_LEVEL + ofs]; + printf (" OP%i: KSL = 0x%x TL = 0x%.2x", i, + (val >> 6) & 0x03, val & 0x3f); + val = inst->data[KSL_LEVEL + ofs + 1]; + printf ("\011OP%i: KSL = 0x%x TL = 0x%.2x\n", i + 1, + (val >> 6) & 0x03, val & 0x3f); + val = inst->data[ATTACK_DECAY + ofs]; + printf (" OP%i: AR = 0x%x DL = 0x%x", i, + (val >> 4) & 0x0f, val & 0x0f); + val = inst->data[ATTACK_DECAY + ofs + 1]; + printf ("\011OP%i: AR = 0x%x DL = 0x%x\n", i + 1, + (val >> 4) & 0x0f, val & 0x0f); + val = inst->data[SUSTAIN_RELEASE + ofs]; + printf (" OP%i: SL = 0x%x RR = 0x%x", i, + (val >> 4) & 0x0f, val & 0x0f); + val = inst->data[SUSTAIN_RELEASE + ofs + 1]; + printf ("\011OP%i: SL = 0x%x RR = 0x%x\n", i + 1, + (val >> 4) & 0x0f, val & 0x0f); + val = inst->data[WAVE_SELECT + ofs]; + printf (" OP%i: WS = 0x%x", i, val & 0x07); + val = inst->data[WAVE_SELECT + ofs + 1]; + printf ("\011\011OP%i: WS = 0x%x\n", i + 1, val & 0x07); + val = inst->data[CONNECTION + ofs]; + printf (" FB = 0x%x, %s\n", (val >> 1) & 0x07, + val & (1 << 0) ? "parallel" : "serial"); + i += 2; + ofs += OFFSET_4OP; } - while (i == (fm_instr->type == FM_PATCH_OPL3) << 1); + while (i == (type == FM_PATCH_OPL3) << 1); printf (" Extended data:\n" " ED = %.3i EA = %.3i CS = %.3i TR = %.3i\n" " FD = %.3i MO = %.3i FK = %.3i\n", - fm_instr->echo_delay, fm_instr->echo_atten, fm_instr->chorus_spread, fm_instr->trnsps, - fm_instr->fix_dur, fm_instr->modes, fm_instr->fix_key); -} - -/* - * Check the result of the previous instr event - */ -static int -check_result (int evtype) { - snd_seq_event_t *pev; - int err; - - for (;;) { - if ((err = snd_seq_event_input (seq_handle, &pev)) < 0) { - fprintf (stderr, "Unable to read event input: %s\n", - snd_strerror (err)); - return -ENXIO; - } - if (pev->type == SND_SEQ_EVENT_RESULT && pev->data.result.event == evtype) - break; - snd_seq_free_event (pev); - } - err = pev->data.result.result; - snd_seq_free_event (pev); - - return err; + inst->extension[ECHO_DELAY], inst->extension[ECHO_ATTEN], + inst->extension[CHORUS_SPREAD], inst->extension[TRNSPS], + inst->extension[FIX_DUR], inst->extension[MODES], + inst->extension[FIX_KEY]); } /* * Send patch to destination port */ static int -load_patch (fm_instrument_t * fm_instr, int bank, int prg, char *name) { - snd_instr_header_t *put; - snd_seq_instr_t id; - snd_seq_event_t ev; - - size_t size; - int err; - - if (verbose > 1) { - printf ("%.3i: [OPL%i] %s\n", prg, fm_instr->type == FM_PATCH_OPL3 ? 3 : 2, name); - show_op (fm_instr); - } - - if ((err = snd_instr_fm_convert_to_stream (fm_instr, name, &put, &size)) < 0) { - fprintf (stderr, "Unable to convert instrument %.3i to stream: %s\n", - prg, snd_strerror (err)); - return -1; - } - memset(&id, 0, sizeof(id)); - id.std = SND_SEQ_INSTR_TYPE2_OPL2_3; - id.prg = prg; - id.bank = bank; - snd_instr_header_set_id(put, &id); - - /* build event */ - memset (&ev, 0, sizeof (ev)); - ev.source.client = seq_client; - ev.source.port = seq_port; - ev.dest.client = seq_dest_client; - ev.dest.port = seq_dest_port; - - ev.flags = SND_SEQ_EVENT_LENGTH_VARUSR; - ev.queue = SND_SEQ_QUEUE_DIRECT; - -__again: - ev.type = SND_SEQ_EVENT_INSTR_PUT; - ev.data.ext.len = size; - ev.data.ext.ptr = put; - - if ((err = snd_seq_event_output (seq_handle, &ev)) < 0) { - fprintf (stderr, "Unable to write an instrument %.3i put event: %s\n", - prg, snd_strerror (err)); +load_patch (struct sbi_patch * inst) { + + ssize_t ret; + ret = snd_hwdep_write(handle, inst, sizeof(*inst)); + if (ret != sizeof(*inst)) { + if (!quiet) + fprintf (stderr, "Unable to write an instrument %.3i put event: %s\n", + inst->prog, snd_strerror (ret)); return -1; } - if ((err = snd_seq_drain_output (seq_handle)) < 0) { - fprintf (stderr, "Unable to write instrument %.3i data: %s\n", prg, - snd_strerror (err)); - return -1; - } - - err = check_result (SND_SEQ_EVENT_INSTR_PUT); - if (err >= 0) { - if (verbose) - printf ("Loaded instrument %.3i, bank %.3i: %s\n", prg, bank, name); - return 0; - } else if (err == -EBUSY) { - snd_instr_header_t *remove; - - snd_instr_header_alloca(&remove); - snd_instr_header_set_cmd(remove, SND_SEQ_INSTR_FREE_CMD_SINGLE); - snd_instr_header_set_id(remove, snd_instr_header_get_id(put)); - - /* remove instrument */ - ev.type = SND_SEQ_EVENT_INSTR_FREE; - ev.data.ext.len = snd_instr_header_sizeof(); - ev.data.ext.ptr = remove; - - if ((err = snd_seq_event_output (seq_handle, &ev)) < 0) { - fprintf (stderr, "Unable to write an instrument %.3i free event: %s\n", - prg, snd_strerror (err)); - return -1; - } - - if ((err = snd_seq_drain_output (seq_handle)) < 0) { - fprintf (stderr, "Unable to write instrument %.3i data: %s\n", prg, - snd_strerror (err)); - return -1; - } - - if ((err = check_result (SND_SEQ_EVENT_INSTR_FREE)) < 0) { - fprintf (stderr, "Instrument %.3i, bank %.3i - free error: %s\n", - prg, bank, snd_strerror (err)); - return -1; - } - goto __again; - } - - fprintf (stderr, "Instrument %.3i, bank %.3i - put error: %s\n", - prg, bank, snd_strerror (err)); - return -1; + if (verbose) + printf ("Loaded instrument %.3i, bank %.3i: %s\n", + inst->prog, inst->bank, inst->name); + return 0; } /* @@ -369,66 +234,45 @@ __again: */ static void load_sb (int bank, int fd) { - int len, i; + int len; int prg; - - sbi_inst_t sbi_instr; - fm_instrument_t fm_instr; + struct sbi_patch inst; int fm_instr_type; - len = (file_type == SBI_FILE_TYPE_4OP) ? DATA_LEN_4OP : DATA_LEN_2OP; + len = (file_type == FM_PATCH_OPL3) ? DATA_LEN_4OP : DATA_LEN_2OP; for (prg = 0;; prg++) { - if (read (fd, &sbi_instr.header, sizeof (sbi_header_t)) < (ssize_t)sizeof (sbi_header_t)) + inst.prog = prg; + inst.bank = bank; + + if (read (fd, inst.key, 4) != 4) break; - if (!strncmp (sbi_instr.header.key, "SBI\032", 4) || !strncmp (sbi_instr.header.key, "2OP\032", 4)) { + if (!memcmp (inst.key, "SBI\032", 4) || !memcmp (inst.key, "2OP\032", 4)) { fm_instr_type = FM_PATCH_OPL2; - } else if (!strncmp (sbi_instr.header.key, "4OP\032", 4)) { + } else if (!strncmp (inst.key, "4OP\032", 4)) { fm_instr_type = FM_PATCH_OPL3; } else { - fm_instr_type = 0; if (verbose) printf ("%.3i: wrong instrument key!\n", prg); + fm_instr_type = FM_PATCH_UNKNOWN; } - if (read (fd, &sbi_instr.data, len) < len) + if (read (fd, &inst.name, sizeof(inst.name)) != sizeof(inst.name) || + read (fd, &inst.extension, sizeof(inst.extension)) != sizeof(inst.extension) || + read (fd, &inst.data, len) != len) break; - if (fm_instr_type == 0) + if (fm_instr_type == FM_PATCH_UNKNOWN) continue; - memset (&fm_instr, 0, sizeof (fm_instr)); - fm_instr.type = fm_instr_type; - - for (i = 0; i < 2; i++) { - fm_instr.op[i].am_vib = sbi_instr.data[AM_VIB + i]; - fm_instr.op[i].ksl_level = sbi_instr.data[KSL_LEVEL + i]; - fm_instr.op[i].attack_decay = sbi_instr.data[ATTACK_DECAY + i]; - fm_instr.op[i].sustain_release = sbi_instr.data[SUSTAIN_RELEASE + i]; - fm_instr.op[i].wave_select = sbi_instr.data[WAVE_SELECT + i]; - } - fm_instr.feedback_connection[0] = sbi_instr.data[CONNECTION]; - - if (fm_instr_type == FM_PATCH_OPL3) { - for (i = 0; i < 2; i++) { - fm_instr.op[i + 2].am_vib = sbi_instr.data[OFFSET_4OP + AM_VIB + i]; - fm_instr.op[i + 2].ksl_level = sbi_instr.data[OFFSET_4OP + KSL_LEVEL + i]; - fm_instr.op[i + 2].attack_decay = sbi_instr.data[OFFSET_4OP + ATTACK_DECAY + i]; - fm_instr.op[i + 2].sustain_release = sbi_instr.data[OFFSET_4OP + SUSTAIN_RELEASE + i]; - fm_instr.op[i + 2].wave_select = sbi_instr.data[OFFSET_4OP + WAVE_SELECT + i]; - } - fm_instr.feedback_connection[1] = sbi_instr.data[OFFSET_4OP + CONNECTION]; + if (verbose > 1) { + printf ("%.3i: [%s] %s\n", inst.prog, + fm_instr_type == FM_PATCH_OPL2 ? "OPL2" : "OPL3", + inst.name); + show_op (&inst, fm_instr_type); } - fm_instr.echo_delay = sbi_instr.header.name[ECHO_DELAY]; - fm_instr.echo_atten = sbi_instr.header.name[ECHO_ATTEN]; - fm_instr.chorus_spread = sbi_instr.header.name[CHORUS_SPREAD]; - fm_instr.trnsps = sbi_instr.header.name[TRNSPS]; - fm_instr.fix_dur = sbi_instr.header.name[FIX_DUR]; - fm_instr.modes = sbi_instr.header.name[MODES]; - fm_instr.fix_key = sbi_instr.header.name[FIX_KEY]; - - if (load_patch (&fm_instr, bank, prg, sbi_instr.header.name) < 0) + if (load_patch (&inst) < 0) break; } return; @@ -440,113 +284,124 @@ load_sb (int bank, int fd) { static int load_file (int bank, char *filename) { int fd; + char path[1024]; + char *name; - if (*filename == '/') - fd = open (filename, O_RDONLY); - else { - char path[1024]; + if (*filename != '/') { snprintf(path, sizeof(path), "%s/%s", patchdir, filename); - fd = open (path, O_RDONLY); + name = path; + } else { + name = filename; } - if (fd == -1) { - perror (filename); + fd = open (name, O_RDONLY); + if (fd < 0) { + /* try to guess from the interface name */ + const char *ext = iface == SND_HWDEP_IFACE_OPL2 ? "sb" : "o3"; + if (*filename != '/') + snprintf(path, sizeof(path), "%s/%s.%s", patchdir, filename, ext); + else + snprintf(path, sizeof(path), "%s.%s", filename, ext); + name = path; + fd = open (name, O_RDONLY); + if (fd < 0) { + if (!quiet) + perror (filename); return -1; + } } + /* correct file type if not set explicitly */ + if (file_type == FM_PATCH_UNKNOWN) + file_type = iface == SND_HWDEP_IFACE_OPL2 ? FM_PATCH_OPL2 : FM_PATCH_OPL3; + + if (verbose) + fprintf (stderr, "Loading from %s\n", name); + load_sb(bank, fd); close (fd); return 0; } -/* - * Parse port description - */ -static int -parse_portdesc (char *portdesc) { - char *astr; - char *cp; - int a[ADDR_PARTS]; - int count; - - if (portdesc == NULL) - return -1; - - for (astr = strtok (portdesc, SEP); astr; astr = strtok (NULL, SEP)) { - for (cp = astr, count = 0; cp && *cp; cp++) { - if (count < ADDR_PARTS) - a[count++] = atoi (cp); - cp = strchr (cp, ':'); - if (cp == NULL) - break; - } - - if (count == 2) { - seq_dest_client = a[0]; - seq_dest_port = a[1]; - } else { - printf ("Addresses in %d parts not supported yet\n", count); - return -1; - } - } - return 0; +static void +clear_patches (void) +{ + snd_hwdep_ioctl(handle, SNDRV_DM_FM_IOCTL_CLEAR_PATCHES, 0); } /* - * Open sequencer, create client port and - * subscribe client to destination port + * Open a hwdep device */ -static int -init_client () { - char name[64]; - snd_seq_port_subscribe_t *sub; - snd_seq_addr_t addr; +static int open_hwdep (const char *name) +{ int err; - - if ((err = snd_seq_open (&seq_handle, "hw", SND_SEQ_OPEN_DUPLEX, 0)) < 0) { - fprintf (stderr, "Could not open sequencer: %s\n", snd_strerror (err)); - return -1; + snd_hwdep_info_t *info; + + if ((err = snd_hwdep_open (&handle, name, SND_HWDEP_OPEN_WRITE)) < 0) + return err; + + snd_hwdep_info_alloca(&info); + if (!snd_hwdep_info (handle, info)) { + iface = snd_hwdep_info_get_iface(info); + if (iface == SND_HWDEP_IFACE_OPL2 || + iface == SND_HWDEP_IFACE_OPL3 || + iface == SND_HWDEP_IFACE_OPL4) + return 0; } + snd_hwdep_close(handle); + handle = NULL; + return -EINVAL; +} - seq_client = snd_seq_client_id (seq_handle); - if (seq_client < 0) { - snd_seq_close (seq_handle); - fprintf (stderr, "Unable to determine my client number: %s\n", - snd_strerror (err)); - return -1; - } +static int +init_hwdep (const char *name) { - sprintf (name, "sbiload - %i", getpid ()); - if ((err = snd_seq_set_client_name (seq_handle, name)) < 0) { - snd_seq_close (seq_handle); - fprintf (stderr, "Unable to set client info: %s\n", - snd_strerror (err)); + int err; + char tmpname[16]; + + if (!name || !*name) { + /* auto probe */ + int card = -1; + snd_ctl_t *ctl; + + while (!snd_card_next(&card) && card >= 0) { + int dev; + sprintf(tmpname, "hw:%d", card); + if (snd_ctl_open(&ctl, tmpname, 0) < 0) + continue; + dev = -1; + while (!snd_ctl_hwdep_next_device(ctl, &dev) && dev >= 0) { + sprintf(tmpname, "hw:%d,%d", card, dev); + if (!open_hwdep(tmpname)) { + snd_ctl_close(ctl); + return 0; + } + } + snd_ctl_close(ctl); + } + if (!quiet) + fprintf (stderr, "Can't find any OPL3 hwdep device\n"); return -1; } - if ((seq_port = snd_seq_create_simple_port (seq_handle, "Output", - SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_WRITE, - SND_SEQ_PORT_TYPE_SPECIFIC)) < 0) { - snd_seq_close (seq_handle); - fprintf (stderr, "Unable to create a client port: %s\n", - snd_strerror (seq_port)); - return -1; + if (*name == '/') { + /* guess card and device numbers - for convenience to user + * from udev rules + */ + int card, device; + if (sscanf(name, "/dev/snd/hwC%dD%d", &card, &device) == 2) { + if (card >= 0 && card <= 32 && device >= 0 && device <= 32) { + sprintf(tmpname, "hw:%d,%d", card, device); + name = tmpname; /* override */ + } + } } - snd_seq_port_subscribe_alloca(&sub); - addr.client = seq_client; - addr.port = seq_port; - snd_seq_port_subscribe_set_sender(sub, &addr); - addr.client = seq_dest_client; - addr.port = seq_dest_port; - snd_seq_port_subscribe_set_dest(sub, &addr); - snd_seq_port_subscribe_set_exclusive(sub, 1); - - if ((err = snd_seq_subscribe_port (seq_handle, sub)) < 0) { - snd_seq_close (seq_handle); - fprintf (stderr, "Unable to subscribe destination port: %s\n", - snd_strerror (errno)); + if ((err = open_hwdep (name)) < 0) { + if (!quiet) + fprintf (stderr, "Could not open hwdep %s: %s\n", + name, snd_strerror (err)); return -1; } @@ -558,42 +413,30 @@ init_client () { * and close sequencer */ static void -finish_client () +finish_hwdep () { - snd_seq_port_subscribe_t *sub; - snd_seq_addr_t addr; - int err; - - snd_seq_port_subscribe_alloca(&sub); - addr.client = seq_client; - addr.port = seq_port; - snd_seq_port_subscribe_set_sender(sub, &addr); - addr.client = seq_dest_client; - addr.port = seq_dest_port; - snd_seq_port_subscribe_set_dest(sub, &addr); - if ((err = snd_seq_unsubscribe_port (seq_handle, sub)) < 0) { - fprintf (stderr, "Unable to unsubscribe destination port: %s\n", - snd_strerror (errno)); - } - snd_seq_close (seq_handle); + snd_hwdep_close(handle); + handle = NULL; } /* * Load a .SBI FM instrument patch * sbiload [-p client:port] [-l] [-P path] [-v level] instfile drumfile * - * -p, --port=client:port - An ALSA client and port number to use - * -4 --opl3 - four operators file type - * -l, --list - List possible output ports that could be used + * -D, --device=name - An ALSA hwdep name to use + * -2 --opl2 - two operators file type (*.sb) + * -4 --opl3 - four operators file type (*.o3) * -P, --path=path - Specify the patch path * -v, --verbose=level - Verbose level + * -q, --quiet - Be quiet, no error/warning messages */ int main (int argc, char **argv) { char opts[NELEM (long_opts) * 2 + 1]; - char *portdesc; + char *name; char *cp; int c; + int clear = 0; struct option *op; /* Build up the short option string */ @@ -604,7 +447,7 @@ main (int argc, char **argv) { *cp++ = ':'; } - portdesc = NULL; + name = NULL; /* Deal with the options */ for (;;) { @@ -613,21 +456,29 @@ main (int argc, char **argv) { break; switch (c) { - case 'p': - portdesc = optarg; + case 'D': + name = optarg; + break; + case 'c': + clear = 1; + break; + case '2': + file_type = FM_PATCH_OPL2; break; case '4': - file_type = SBI_FILE_TYPE_4OP; + file_type = FM_PATCH_OPL3; + break; + case 'q': + quiet = 1; + verbose = 0; break; case 'v': + quiet = 0; verbose = atoi (optarg); break; case 'V': show_version(); exit (1); - case 'l': - show_list (); - exit (0); case 'P': patchdir = optarg; break; @@ -637,46 +488,33 @@ main (int argc, char **argv) { } } - if (portdesc == NULL) { - portdesc = getenv ("ALSA_OUT_PORT"); - if (portdesc == NULL) { - fprintf (stderr, "No client/port specified.\n" - "You must supply one with the -p option or with the\n" - "environment variable ALSA_OUT_PORT\n"); - exit (1); - } - } - - /* Parse port description to dest_client and dest_port */ - if (parse_portdesc (portdesc) < 0) { + if (init_hwdep (name) < 0) { return 1; } - /* Initialize client and subscribe to destination port */ - if (init_client () < 0) { - return 1; - } + clear_patches (); + if (clear) + goto done; /* Process instrument and drum file */ - if (optind < argc) { - if (load_file (0, argv[optind++]) < 0) { - finish_client(); - return 1; - } - } else { - fprintf(stderr, "Warning: instrument file was not specified\n"); + if (optind < argc) + name = argv[optind++]; + else + name = "std"; + if (load_file (0, name) < 0) { + finish_hwdep(); + return 1; } - if (optind < argc) { - if (load_file (128, argv[optind]) < 0) { - finish_client(); - return 1; - } - } else { - fprintf(stderr, "Warning: drum file was not specified\n"); + if (optind < argc) + name = argv[optind]; + else + name = "drums"; + if (load_file (128, name) < 0) { + finish_hwdep(); + return 1; } - /* Unsubscribe destination port and close client */ - finish_client(); - + done: + finish_hwdep(); return 0; } -- 2.47.1