[alsa-devel] Testers Wanted: OPL3 hwdep loader interface
Hi,
one of big pending patches on my local tree is the removal of seq-instr interface. The only real user of instrument layer is OPL3 patch loader, and I'd like to move this to hwdep interface.
The patch below is to move the OPL3 patch loading to hwdep device. Two more patches will follow in other mails for alsa-lib addition and sbiload change.
Can someone test whether these patches work? Unfortunately, I have no hardware with OPL3 at this moment around here.
Thanks,
Takashi
diff -r 88d273f360dd drivers/opl3/opl3_lib.c --- a/drivers/opl3/opl3_lib.c Fri Oct 26 15:48:22 2007 +0200 +++ b/drivers/opl3/opl3_lib.c Fri Oct 26 16:09:56 2007 +0200 @@ -327,6 +327,7 @@ static int snd_opl3_free(struct snd_opl3 snd_assert(opl3 != NULL, return -ENXIO); if (opl3->private_free) opl3->private_free(opl3); + snd_opl3_clear_patches(opl3); release_and_free_resource(opl3->res_l_port); release_and_free_resource(opl3->res_r_port); kfree(opl3); @@ -521,6 +522,7 @@ int snd_opl3_hwdep_new(struct snd_opl3 * /* operators - only ioctl */ hw->ops.open = snd_opl3_open; hw->ops.ioctl = snd_opl3_ioctl; + hw->ops.write = snd_opl3_write; hw->ops.release = snd_opl3_release;
opl3->seq_dev_num = seq_device; diff -r 88d273f360dd drivers/opl3/opl3_midi.c --- a/drivers/opl3/opl3_midi.c Fri Oct 26 15:48:22 2007 +0200 +++ b/drivers/opl3/opl3_midi.c Fri Oct 26 16:09:56 2007 +0200 @@ -289,8 +289,6 @@ void snd_opl3_note_on(void *p, int note, void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan) { struct snd_opl3 *opl3; - struct snd_seq_instr wanted; - struct snd_seq_kinstr *kinstr; int instr_4op;
int voice; @@ -306,11 +304,13 @@ void snd_opl3_note_on(void *p, int note, unsigned char voice_offset; unsigned short opl3_reg; unsigned char reg_val; + unsigned char prg, bank;
int key = note; unsigned char fnum, blocknum; int i;
+ struct fm_patch *patch; struct fm_instrument *fm; unsigned long flags;
@@ -320,19 +320,17 @@ void snd_opl3_note_on(void *p, int note, snd_printk("Note on, ch %i, inst %i, note %i, vel %i\n", chan->number, chan->midi_program, note, vel); #endif - wanted.cluster = 0; - wanted.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3;
/* in SYNTH mode, application takes care of voices */ /* in SEQ mode, drum voice numbers are notes on drum channel */ if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { if (chan->drum_channel) { /* percussion instruments are located in bank 128 */ - wanted.bank = 128; - wanted.prg = note; + bank = 128; + prg = note; } else { - wanted.bank = chan->gm_bank_select; - wanted.prg = chan->midi_program; + bank = chan->gm_bank_select; + prg = chan->midi_program; } } else { /* Prepare for OSS mode */ @@ -340,8 +338,8 @@ void snd_opl3_note_on(void *p, int note, return;
/* OSS instruments are located in bank 127 */ - wanted.bank = 127; - wanted.prg = chan->midi_program; + bank = 127; + prg = chan->midi_program; }
spin_lock_irqsave(&opl3->voice_lock, flags); @@ -353,15 +351,14 @@ void snd_opl3_note_on(void *p, int note, }
__extra_prg: - kinstr = snd_seq_instr_find(opl3->ilist, &wanted, 1, 0); - if (kinstr == NULL) { + patch = snd_opl3_find_patch(opl3, prg, bank, 0); + if (!patch) { spin_unlock_irqrestore(&opl3->voice_lock, flags); return; }
- fm = KINSTR_DATA(kinstr); - - switch (fm->type) { + fm = &patch->inst; + switch (patch->type) { case FM_PATCH_OPL2: instr_4op = 0; break; @@ -371,14 +368,12 @@ void snd_opl3_note_on(void *p, int note, break; } default: - snd_seq_instr_free_use(opl3->ilist, kinstr); spin_unlock_irqrestore(&opl3->voice_lock, flags); return; } - #ifdef DEBUG_MIDI snd_printk(" --> OPL%i instrument: %s\n", - instr_4op ? 3 : 2, kinstr->name); + instr_4op ? 3 : 2, patch->name); #endif /* in SYNTH mode, application takes care of voices */ /* in SEQ mode, allocate voice on free OPL3 channel */ @@ -569,8 +564,6 @@ void snd_opl3_note_on(void *p, int note, /* get extra pgm, but avoid possible loops */ extra_prg = (extra_prg) ? 0 : fm->modes;
- snd_seq_instr_free_use(opl3->ilist, kinstr); - /* do the bookkeeping */ vp->time = opl3->use_time++; vp->note = key; @@ -601,12 +594,12 @@ void snd_opl3_note_on(void *p, int note, /* allocate extra program if specified in patch library */ if (extra_prg) { if (extra_prg > 128) { - wanted.bank = 128; + bank = 128; /* percussions start at 35 */ - wanted.prg = extra_prg - 128 + 35 - 1; + prg = extra_prg - 128 + 35 - 1; } else { - wanted.bank = 0; - wanted.prg = extra_prg - 1; + bank = 0; + prg = extra_prg - 1; } #ifdef DEBUG_MIDI snd_printk(" *** allocating extra program\n"); diff -r 88d273f360dd drivers/opl3/opl3_oss.c --- a/drivers/opl3/opl3_oss.c Fri Oct 26 15:48:22 2007 +0200 +++ b/drivers/opl3/opl3_oss.c Fri Oct 26 16:09:56 2007 +0200 @@ -195,17 +195,6 @@ static int snd_opl3_close_seq_oss(struct
/* load patch */
-/* offsets for SBI params */ -#define AM_VIB 0 -#define KSL_LEVEL 2 -#define ATTACK_DECAY 4 -#define SUSTAIN_RELEASE 6 -#define WAVE_SELECT 8 - -/* offset for SBI instrument */ -#define CONNECTION 10 -#define OFFSET_4OP 11 - /* from sound_config.h */ #define SBFM_MAXINSTR 256
@@ -213,112 +202,42 @@ static int snd_opl3_load_patch_seq_oss(s const char __user *buf, int offs, int count) { struct snd_opl3 *opl3; - int err = -EINVAL; + struct sbi_instrument sbi; + char name[32]; + int err, type;
snd_assert(arg != NULL, return -ENXIO); opl3 = arg->private_data;
- if ((format == FM_PATCH) || (format == OPL3_PATCH)) { - struct sbi_instrument sbi; + if (format == FM_PATCH) + type = FM_PATCH_OPL2; + else if (format == OPL3_PATCH) + type = FM_PATCH_OPL3; + else + return -EINVAL;
- size_t size; - struct snd_seq_instr_header *put; - struct snd_seq_instr_data *data; - struct fm_xinstrument *xinstr; + if (count < (int)sizeof(sbi)) { + snd_printk("FM Error: Patch record too short\n"); + return -EINVAL; + } + if (copy_from_user(&sbi, buf, sizeof(sbi))) + return -EFAULT;
- struct snd_seq_event ev; - int i; + if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) { + snd_printk("FM Error: Invalid instrument number %d\n", + sbi.channel); + return -EINVAL; + }
- mm_segment_t fs; + memset(name, 0, sizeof(name)); + sprintf(name, "Chan%d", sbi.channel);
- if (count < (int)sizeof(sbi)) { - snd_printk("FM Error: Patch record too short\n"); - return -EINVAL; - } - if (copy_from_user(&sbi, buf, sizeof(sbi))) - return -EFAULT; + err = snd_opl3_load_patch(opl3, sbi.channel, 127, type, name, NULL, + sbi.operators); + if (err < 0) + return err;
- if (sbi.channel < 0 || sbi.channel >= SBFM_MAXINSTR) { - snd_printk("FM Error: Invalid instrument number %d\n", sbi.channel); - return -EINVAL; - } - - size = sizeof(*put) + sizeof(struct fm_xinstrument); - put = kzalloc(size, GFP_KERNEL); - if (put == NULL) - return -ENOMEM; - /* build header */ - data = &put->data; - data->type = SNDRV_SEQ_INSTR_ATYPE_DATA; - strcpy(data->data.format, SNDRV_SEQ_INSTR_ID_OPL2_3); - /* build data section */ - xinstr = (struct fm_xinstrument *)(data + 1); - xinstr->stype = FM_STRU_INSTR; - - for (i = 0; i < 2; i++) { - xinstr->op[i].am_vib = sbi.operators[AM_VIB + i]; - xinstr->op[i].ksl_level = sbi.operators[KSL_LEVEL + i]; - xinstr->op[i].attack_decay = sbi.operators[ATTACK_DECAY + i]; - xinstr->op[i].sustain_release = sbi.operators[SUSTAIN_RELEASE + i]; - xinstr->op[i].wave_select = sbi.operators[WAVE_SELECT + i]; - } - xinstr->feedback_connection[0] = sbi.operators[CONNECTION]; - - if (format == OPL3_PATCH) { - xinstr->type = FM_PATCH_OPL3; - for (i = 0; i < 2; i++) { - xinstr->op[i+2].am_vib = sbi.operators[OFFSET_4OP + AM_VIB + i]; - xinstr->op[i+2].ksl_level = sbi.operators[OFFSET_4OP + KSL_LEVEL + i]; - xinstr->op[i+2].attack_decay = sbi.operators[OFFSET_4OP + ATTACK_DECAY + i]; - xinstr->op[i+2].sustain_release = sbi.operators[OFFSET_4OP + SUSTAIN_RELEASE + i]; - xinstr->op[i+2].wave_select = sbi.operators[OFFSET_4OP + WAVE_SELECT + i]; - } - xinstr->feedback_connection[1] = sbi.operators[OFFSET_4OP + CONNECTION]; - } else { - xinstr->type = FM_PATCH_OPL2; - } - - put->id.instr.std = SNDRV_SEQ_INSTR_TYPE2_OPL2_3; - put->id.instr.bank = 127; - put->id.instr.prg = sbi.channel; - put->cmd = SNDRV_SEQ_INSTR_PUT_CMD_CREATE; - - memset (&ev, 0, sizeof(ev)); - ev.source.client = SNDRV_SEQ_CLIENT_OSS; - ev.dest = arg->addr; - - ev.flags = SNDRV_SEQ_EVENT_LENGTH_VARUSR; - ev.queue = SNDRV_SEQ_QUEUE_DIRECT; - - fs = snd_enter_user(); - __again: - ev.type = SNDRV_SEQ_EVENT_INSTR_PUT; - ev.data.ext.len = size; - ev.data.ext.ptr = put; - - err = snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, &ev, - opl3->seq_client, 0, 0); - if (err == -EBUSY) { - struct snd_seq_instr_header remove; - - memset (&remove, 0, sizeof(remove)); - remove.cmd = SNDRV_SEQ_INSTR_FREE_CMD_SINGLE; - remove.id.instr = put->id.instr; - - /* remove instrument */ - ev.type = SNDRV_SEQ_EVENT_INSTR_FREE; - ev.data.ext.len = sizeof(remove); - ev.data.ext.ptr = &remove; - - snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, &ev, - opl3->seq_client, 0, 0); - goto __again; - } - snd_leave_user(fs); - - kfree(put); - } - return err; + return sizeof(sbi); }
/* ioctl */ diff -r 88d273f360dd drivers/opl3/opl3_seq.c --- a/drivers/opl3/opl3_seq.c Fri Oct 26 15:48:22 2007 +0200 +++ b/drivers/opl3/opl3_seq.c Fri Oct 26 16:09:56 2007 +0200 @@ -152,15 +152,7 @@ static int snd_opl3_synth_event_input(st { struct snd_opl3 *opl3 = private_data;
- if (ev->type >= SNDRV_SEQ_EVENT_INSTR_BEGIN && - ev->type <= SNDRV_SEQ_EVENT_INSTR_CHANGE) { - if (direct) { - snd_seq_instr_event(&opl3->fm_ops, opl3->ilist, ev, - opl3->seq_client, atomic, hop); - } - } else { - snd_midi_process_event(&opl3_ops, ev, opl3->chset); - } + snd_midi_process_event(&opl3_ops, ev, opl3->chset); return 0; }
@@ -249,16 +241,6 @@ static int snd_opl3_seq_new_device(struc return err; }
- /* initialize instrument list */ - opl3->ilist = snd_seq_instr_list_new(); - if (opl3->ilist == NULL) { - snd_seq_delete_kernel_client(client); - opl3->seq_client = -1; - return -ENOMEM; - } - opl3->ilist->flags = SNDRV_SEQ_INSTR_FLG_DIRECT; - snd_seq_fm_init(&opl3->fm_ops, NULL); - /* setup system timer */ init_timer(&opl3->tlist); opl3->tlist.function = snd_opl3_timer_func; @@ -287,8 +269,6 @@ static int snd_opl3_seq_delete_device(st snd_seq_delete_kernel_client(opl3->seq_client); opl3->seq_client = -1; } - if (opl3->ilist) - snd_seq_instr_list_free(&opl3->ilist); return 0; }
diff -r 88d273f360dd drivers/opl3/opl3_synth.c --- a/drivers/opl3/opl3_synth.c Fri Oct 26 15:48:22 2007 +0200 +++ b/drivers/opl3/opl3_synth.c Fri Oct 26 16:09:56 2007 +0200 @@ -186,6 +186,170 @@ int snd_opl3_release(struct snd_hwdep * mutex_unlock(&opl3->access_mutex);
return 0; +} + +/* + * write the device - load patches + */ +long snd_opl3_write(struct snd_hwdep *hw, const char __user *buf, long count, + loff_t *offset) +{ + struct snd_opl3 *opl3 = hw->private_data; + long result = 0; + int err = 0; + struct sbi_patch inst; + + while (count >= sizeof(inst)) { + unsigned char type; + if (copy_from_user(&inst, buf, sizeof(inst))) + return -EFAULT; + if (!memcmp(inst.key, FM_KEY_SBI, 4) || + !memcmp(inst.key, FM_KEY_2OP, 4)) + type = FM_PATCH_OPL2; + else if (!memcmp(inst.key, FM_KEY_4OP, 4)) + type = FM_PATCH_OPL3; + else /* invalid type */ + break; + err = snd_opl3_load_patch(opl3, inst.prog, inst.bank, type, + inst.name, inst.extension, + inst.data); + if (err < 0) + break; + result += sizeof(inst); + count -= sizeof(inst); + } + return result > 0 ? result : err; +} + + +/* + * Patch management + */ + +/* offsets for SBI params */ +#define AM_VIB 0 +#define KSL_LEVEL 2 +#define ATTACK_DECAY 4 +#define SUSTAIN_RELEASE 6 +#define WAVE_SELECT 8 + +/* offset for SBI instrument */ +#define CONNECTION 10 +#define OFFSET_4OP 11 + +/* + * load a patch, obviously. + * + * loaded on the given program and bank numbers with the given type + * (FM_PATCH_OPLx). + * data is the pointer of SBI record _without_ header (key and name). + * name is the name string of the patch. + * ext is the extension data of 7 bytes long (stored in name of SBI + * data up to offset 25), or NULL to skip. + * return 0 if successful or a negative error code. + */ +int snd_opl3_load_patch(struct snd_opl3 *opl3, + int prog, int bank, int type, + const char *name, + const unsigned char *ext, + const unsigned char *data) +{ + struct fm_patch *patch; + int i; + + patch = snd_opl3_find_patch(opl3, prog, bank, 1); + if (!patch) + return -ENOMEM; + + patch->type = type; + + for (i = 0; i < 2; i++) { + patch->inst.op[i].am_vib = data[AM_VIB + i]; + patch->inst.op[i].ksl_level = data[KSL_LEVEL + i]; + patch->inst.op[i].attack_decay = data[ATTACK_DECAY + i]; + patch->inst.op[i].sustain_release = data[SUSTAIN_RELEASE + i]; + patch->inst.op[i].wave_select = data[WAVE_SELECT + i]; + } + patch->inst.feedback_connection[0] = data[CONNECTION]; + + if (type == FM_PATCH_OPL3) { + for (i = 0; i < 2; i++) { + patch->inst.op[i+2].am_vib = + data[OFFSET_4OP + AM_VIB + i]; + patch->inst.op[i+2].ksl_level = + data[OFFSET_4OP + KSL_LEVEL + i]; + patch->inst.op[i+2].attack_decay = + data[OFFSET_4OP + ATTACK_DECAY + i]; + patch->inst.op[i+2].sustain_release = + data[OFFSET_4OP + SUSTAIN_RELEASE + i]; + patch->inst.op[i+2].wave_select = + data[OFFSET_4OP + WAVE_SELECT + i]; + } + patch->inst.feedback_connection[1] = + data[OFFSET_4OP + CONNECTION]; + } + + if (ext) { + patch->inst.echo_delay = ext[0]; + patch->inst.echo_atten = ext[1]; + patch->inst.chorus_spread = ext[2]; + patch->inst.trnsps = ext[3]; + patch->inst.fix_dur = ext[4]; + patch->inst.modes = ext[5]; + patch->inst.fix_key = ext[6]; + } + + if (name) + strlcpy(patch->name, name, sizeof(patch->name)); + + return 0; +} +EXPORT_SYMBOL(snd_opl3_load_patch); + +/* + * find a patch with the given program and bank numbers, returns its pointer + * if no matching patch is found and create_patch is set, it creates a + * new patch object. + */ +struct fm_patch *snd_opl3_find_patch(struct snd_opl3 *opl3, int prog, int bank, + int create_patch) +{ + /* pretty dumb hash key */ + unsigned int key = (prog + bank) % OPL3_PATCH_HASH_SIZE; + struct fm_patch *patch; + + for (patch = opl3->patch_table[key]; patch; patch = patch->next) { + if (patch->prog == prog && patch->bank == bank) + return patch; + } + if (!create_patch) + return NULL; + + patch = kzalloc(sizeof(*patch), GFP_KERNEL); + if (!patch) + return NULL; + patch->prog = prog; + patch->bank = bank; + patch->next = opl3->patch_table[key]; + opl3->patch_table[key] = patch; + return patch; +} +EXPORT_SYMBOL(snd_opl3_find_patch); + +/* + * Clear all patches of the given OPL3 instance + */ +void snd_opl3_clear_patches(struct snd_opl3 *opl3) +{ + int i; + for (i = 0; i < OPL3_PATCH_HASH_SIZE; i++) { + struct fm_patch *patch, *next; + for (patch = opl3->patch_table[i]; patch; patch = next) { + next = patch->next; + kfree(patch); + } + } + memset(opl3->patch_table, 0, sizeof(opl3->patch_table)); }
/* ------------------------------ */ diff -r 88d273f360dd include/asound_fm.h --- a/include/asound_fm.h Fri Oct 26 15:48:22 2007 +0200 +++ b/include/asound_fm.h Fri Oct 26 16:09:56 2007 +0200 @@ -112,4 +112,21 @@ struct snd_dm_fm_params { #define SNDRV_DM_FM_OSS_IOCTL_SET_MODE 0x24 #define SNDRV_DM_FM_OSS_IOCTL_SET_OPL 0x25
+/* + * Patch Record - fixed size for write + */ + +#define FM_KEY_SBI "SBI\032" +#define FM_KEY_2OP "2OP\032" +#define FM_KEY_4OP "4OP\032" + +struct sbi_patch { + unsigned char prog; + unsigned char bank; + char key[4]; + char name[25]; + char extension[7]; + unsigned char data[32]; +}; + #endif /* __SOUND_ASOUND_FM_H */ diff -r 88d273f360dd include/opl3.h --- a/include/opl3.h Fri Oct 26 15:48:22 2007 +0200 +++ b/include/opl3.h Fri Oct 26 16:09:56 2007 +0200 @@ -63,7 +63,7 @@ #include "seq_oss_legacy.h" #endif #include "seq_device.h" -#include "ainstr_fm.h" +#include "asound_fm.h"
/* * Register numbers for the global registers @@ -240,6 +240,47 @@ struct snd_opl3; struct snd_opl3;
/* + * Instrument record, aka "Patch" + */ + +/* FM operator */ +struct fm_operator { + unsigned char am_vib; + unsigned char ksl_level; + unsigned char attack_decay; + unsigned char sustain_release; + unsigned char wave_select; +} __attribute__((packed)); + +/* Instrument data */ +struct fm_instrument { + struct fm_operator op[4]; + unsigned char feedback_connection[2]; + unsigned char echo_delay; + unsigned char echo_atten; + unsigned char chorus_spread; + unsigned char trnsps; + unsigned char fix_dur; + unsigned char modes; + unsigned char fix_key; +}; + +/* type */ +#define FM_PATCH_OPL2 0x01 /* OPL2 2 operators FM instrument */ +#define FM_PATCH_OPL3 0x02 /* OPL3 4 operators FM instrument */ + +/* Instrument record */ +struct fm_patch { + unsigned char prog; + unsigned char bank; + unsigned char type; + struct fm_instrument inst; + char name[24]; + struct fm_patch *next; +}; + + +/* * A structure to keep track of each hardware voice */ struct snd_opl3_voice { @@ -297,8 +338,8 @@ struct snd_opl3 { struct snd_midi_channel_set * oss_chset; #endif
- struct snd_seq_kinstr_ops fm_ops; - struct snd_seq_kinstr_list *ilist; +#define OPL3_PATCH_HASH_SIZE 32 + struct fm_patch *patch_table[OPL3_PATCH_HASH_SIZE];
struct snd_opl3_voice voices[MAX_OPL3_VOICES]; /* Voices (OPL3 'channel') */ int use_time; /* allocation counter */ @@ -333,8 +374,19 @@ int snd_opl3_open(struct snd_hwdep * hw, int snd_opl3_open(struct snd_hwdep * hw, struct file *file); int snd_opl3_ioctl(struct snd_hwdep * hw, struct file *file, unsigned int cmd, unsigned long arg); +long snd_opl3_write(struct snd_hwdep *hw, const char __user *buf, long count, + loff_t *offset); int snd_opl3_release(struct snd_hwdep * hw, struct file *file);
void snd_opl3_reset(struct snd_opl3 * opl3);
+int snd_opl3_load_patch(struct snd_opl3 *opl3, + int prog, int bank, int type, + const char *name, + const unsigned char *ext, + const unsigned char *data); +struct fm_patch *snd_opl3_find_patch(struct snd_opl3 *opl3, int prog, int bank, + int create_patch); +void snd_opl3_clear_patches(struct snd_opl3 *opl3); + #endif /* __SOUND_OPL3_H */
On 10/26/2007 02:37 PM, Takashi Iwai wrote:
one of big pending patches on my local tree is the removal of seq-instr interface. The only real user of instrument layer is OPL3 patch loader, and I'd like to move this to hwdep interface.
The patch below is to move the OPL3 patch loading to hwdep device. Two more patches will follow in other mails for alsa-lib addition and sbiload change.
Can someone test whether these patches work? Unfortunately, I have no hardware with OPL3 at this moment around here.
Working fine. Applied the patches against alsa-{kernel,lib,tools}-1.0.15.
rene@7ixe4:~$ aplaymidi -l Port Client name Port name 14:0 Midi Through Midi Through Port-0 16:0 ESS ES1978 (Maestro 2E) ESS ES1978 (Maestro 2E) MIDI 20:0 CS4236B CS4236B MIDI 21:0 OPL3 FM synth OPL3 FM Port rene@7ixe4:~$ cat /proc/asound/hwdep 01-00: OPL3 FM rene@7ixe4:~$ sbiload -D hw:1,0 -4 std.o3 drums.o3 rene@7ixe4:~$ aplaymidi -p 21:0 music/midi/jzz30xg.mid
<noise>
If you need/want me to, I can also test against an OPL2 (AdLib clone card) but I assume that'll be okay?
Rene.
On 10/26/2007 08:55 PM, Rene Herman wrote:
Working fine. Applied the patches against alsa-{kernel,lib,tools}-1.0.15.
rene@7ixe4:~$ aplaymidi -l Port Client name Port name 14:0 Midi Through Midi Through Port-0 16:0 ESS ES1978 (Maestro 2E) ESS ES1978 (Maestro 2E) MIDI 20:0 CS4236B CS4236B MIDI 21:0 OPL3 FM synth OPL3 FM Port rene@7ixe4:~$ cat /proc/asound/hwdep 01-00: OPL3 FM rene@7ixe4:~$ sbiload -D hw:1,0 -4 std.o3 drums.o3 rene@7ixe4:~$ aplaymidi -p 21:0 music/midi/jzz30xg.mid
<noise>
If you need/want me to, I can also test against an OPL2 (AdLib clone card) but I assume that'll be okay?
rene@6bap:~$ aplaymidi -l Port Client name Port name 14:0 Midi Through Midi Through Port-0 16:0 OPL2 FM synth OPL2 FM Port rene@6bap:~$ cat /proc/asound/hwdep 00-00: OPL2 FM rene@6bap:~$ sbiload -D hw:0,0 std.sb drums.sb rene@6bap:~$ aplaymidi -p 16:0 midi/jzz30xg.mid
<same noise>
So, OPL2 same deal. However -- there is a problem after all. With this as the source file:
http://members.home.nl/rene.herman/opl3/jzz30xg.mid
using the old sbiload with the sb and o3 patches respectively, produces:
http://members.home.nl/rene.herman/opl3/jzz30xg-cs4236b_opl3-seq-sb.ogg http://members.home.nl/rene.herman/opl3/jzz30xg-cs4236b_opl3-seq-o3.ogg
while with the new hwdep sbiload, I hear:
http://members.home.nl/rene.herman/opl3/jzz30xg-cs4236b_opl3-hwdep-sb.ogg http://members.home.nl/rene.herman/opl3/jzz30xg-cs4236b_opl3-hwdep-o3.ogg
While much less annoying... something's still missing. The former are how they are supposed to be rendered. A problem with the "drums"?
Rene.
At Mon, 29 Oct 2007 08:41:40 +0100, Clemens Ladisch wrote:
Rene Herman wrote:
However -- there is a problem after all. [...] While much less annoying... something's still missing.
Several instruments aren't played at all, at least those with program number 40 and up (strings) and 64 and up (saxophones).
OK, how about the revised patch below to alsa-tools?
thanks,
Takashi
diff -r 9b306261208b seq/sbiload/README --- a/seq/sbiload/README Mon Oct 22 15:16:35 2007 +0200 +++ b/seq/sbiload/README Mon Oct 29 12:01:07 2007 +0100 @@ -1,4 +1,4 @@ This is sbiload - an OPL2/3 FM instrumen -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 uros@kss-loka.si Web: http://www.kss-loka.si/~uros/sbiload.html @@ -14,62 +14,42 @@ By default it will install into /usr/loc 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 + loadsbi [-D device] [-4] [-P path] [-v level] instfile drumfile
- -p, --port=client:port - An ALSA client and port number to use + -D, --device=name - An ALSA hwdep name to use -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
-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. +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.
-First find out what the possibilities are for your system: +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.
-If you run: - - sbiload -l + % cat /proc/asound/hwdep + 00-00: Emux WaveTable + 00-01: OPL3 FM + 01-00: OPL3 FM
-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 +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".
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.
-For example to use port 0 on client 65 to load four-operator FM -instrument - - sbiload -p 65:0 --opl3 std.o3 drums.o3 - -or - - export ALSA_OUT_PORT=65:0 - sbiload --opl3 std.o3 drums.o3 + % sbiload std.o3 drums.o3
Acknowledgements diff -r 9b306261208b seq/sbiload/sbiload.c --- a/seq/sbiload/sbiload.c Mon Oct 22 15:16:35 2007 +0200 +++ b/seq/sbiload/sbiload.c Mon Oct 29 12:01:07 2007 +0100 @@ -1,5 +1,5 @@ /* - * ALSA sequencer SBI FM instrument loader + * ALSA hwdep SBI FM instrument loader * Copyright (c) 2000 Uros Bizjak uros@kss-loka.si * * 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 tiwai@suse.de + * Changed to use hwdep instead of obsoleted seq-instr interface */
#include <errno.h> #include <getopt.h> -#include <alsa/sound/ainstr_fm.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -29,24 +31,12 @@ #include <fcntl.h> #include <errno.h> #include <alsa/asoundlib.h> +#include <alsa/sound/asound_fm.h> #include <sys/time.h> #include <unistd.h>
-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,21 +49,20 @@ 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'}, {"opl3", 0, NULL, '4'}, - {"list", 0, NULL, 'l'}, {"path", HAS_ARG, NULL, 'P'}, {"verbose", HAS_ARG, NULL, 'v'}, {"version", 0, NULL, 'V'}, @@ -83,87 +72,35 @@ 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_OPL2;
/* Default verbose level */ -int verbose = 0; +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;
#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,11 +109,10 @@ show_usage () { show_usage () { char **cpp; static char *msg[] = { - "Usage: sbiload [-p client:port] [-4] [-l] [-P path] [-v level] instfile drumfile", + "Usage: sbiload [-D device] [-4] [-P path] [-v level] instfile drumfile", "", - " -p client:port - A alsa client and port number to send midi to", + " -D device - hwdep device string", " -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", @@ -199,169 +135,86 @@ 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; +load_patch (struct sbi_patch * inst) {
- 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) { + ssize_t ret; + ret = snd_hwdep_write(handle, inst, sizeof(*inst)); + if (ret != sizeof(*inst)) { fprintf (stderr, "Unable to write an instrument %.3i put event: %s\n", - prg, snd_strerror (err)); + 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 +222,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; @@ -461,92 +293,57 @@ load_file (int bank, char *filename) { }
/* - * Parse port description + * Open a hwdep device */ -static int -parse_portdesc (char *portdesc) { - char *astr; - char *cp; - int a[ADDR_PARTS]; - int count; +static int open_hwdep (const char *name) +{ + int err; + snd_hwdep_info_t *info;
- if (portdesc == NULL) - return -1; + if ((err = snd_hwdep_open (&handle, name, SND_HWDEP_OPEN_WRITE)) < 0) + return err;
- 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; - } + snd_hwdep_info_alloca(&info); + if (!snd_hwdep_info (handle, info)) { + snd_hwdep_iface_t iface = snd_hwdep_info_get_iface(info); + if (iface == SND_HWDEP_IFACE_OPL2 || iface == SND_HWDEP_IFACE_OPL3) + return 0; } - return 0; + snd_hwdep_close(handle); + handle = NULL; + return -EINVAL; }
-/* - * Open sequencer, create client port and - * subscribe client to destination port - */ static int -init_client () { - char name[64]; - snd_seq_port_subscribe_t *sub; - snd_seq_addr_t addr; +init_hwdep (const char *name) { + int err; + char tmpname[16];
- 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)); + 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)) + return 0; + } + snd_ctl_close(ctl); + } + fprintf (stderr, "Can't find any OPL3 hwdep device\n"); return -1; }
- 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; - } - - 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)); - 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; - } - - 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) { + fprintf (stderr, "Could not open hwdep %s: %s\n", name, snd_strerror (err)); return -1; }
@@ -558,40 +355,25 @@ 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 + * -D, --device=name - An ALSA hwdep name to use * -4 --opl3 - four operators file type - * -l, --list - List possible output ports that could be used * -P, --path=path - Specify the patch path * -v, --verbose=level - Verbose level */ int main (int argc, char **argv) { char opts[NELEM (long_opts) * 2 + 1]; - char *portdesc; + char *name; char *cp; int c; struct option *op; @@ -604,7 +386,7 @@ main (int argc, char **argv) { *cp++ = ':'; }
- portdesc = NULL; + name = NULL;
/* Deal with the options */ for (;;) { @@ -613,11 +395,11 @@ main (int argc, char **argv) { break;
switch (c) { - case 'p': - portdesc = optarg; + case 'D': + name = optarg; break; case '4': - file_type = SBI_FILE_TYPE_4OP; + file_type = FM_PATCH_OPL3; break; case 'v': verbose = atoi (optarg); @@ -625,9 +407,6 @@ main (int argc, char **argv) { case 'V': show_version(); exit (1); - case 'l': - show_list (); - exit (0); case 'P': patchdir = optarg; break; @@ -637,30 +416,14 @@ 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) { - return 1; - } - - /* Initialize client and subscribe to destination port */ - if (init_client () < 0) { + if (init_hwdep (name) < 0) { return 1; }
/* Process instrument and drum file */ if (optind < argc) { if (load_file (0, argv[optind++]) < 0) { - finish_client(); + finish_hwdep(); return 1; } } else { @@ -668,15 +431,14 @@ main (int argc, char **argv) { } if (optind < argc) { if (load_file (128, argv[optind]) < 0) { - finish_client(); + finish_hwdep(); return 1; } } else { fprintf(stderr, "Warning: drum file was not specified\n"); }
- /* Unsubscribe destination port and close client */ - finish_client(); + finish_hwdep();
return 0; }
On 10/29/2007 10:06 AM, Takashi Iwai wrote:
At Mon, 29 Oct 2007 08:41:40 +0100, Clemens Ladisch wrote:
Several instruments aren't played at all, at least those with program number 40 and up (strings) and 64 and up (saxophones).
OK, how about the revised patch below to alsa-tools?
Yes, I believe my OPL3 is back to its good ol' horrible self again with that version.
Do you by the way know of other, nicer and/or better patches than the supplied std and drums? Couldn't find any, at least not in the correct format it seems.
[ README ]
- sbiload --opl3 std.o3 drums.o3
- % sbiload std.o3 drums.o3
Should the example indeed lose the --opl3?
Rene.
At Mon, 29 Oct 2007 17:51:23 +0100, Rene Herman wrote:
On 10/29/2007 10:06 AM, Takashi Iwai wrote:
At Mon, 29 Oct 2007 08:41:40 +0100, Clemens Ladisch wrote:
Several instruments aren't played at all, at least those with program number 40 and up (strings) and 64 and up (saxophones).
OK, how about the revised patch below to alsa-tools?
Yes, I believe my OPL3 is back to its good ol' horrible self again with that version.
Thanks. Then I'll commit the changes tomorrow until someone finds regressions.
Do you by the way know of other, nicer and/or better patches than the supplied std and drums? Couldn't find any, at least not in the correct format it seems.
Hm, I don't know of others. It'd be fun to play with OPL3 parameter adjustment, though ;)
[ README ]
- sbiload --opl3 std.o3 drums.o3
- % sbiload std.o3 drums.o3
Should the example indeed lose the --opl3?
With the patch in the previous mail, yes, it's needed. But my latest version that will be committed doesn't need --opl3 option. You can invoke like
% sbiload std drums
and the format and the extension will be chosen automatically according to the hwdep iface type.
Takashi
On 10/29/2007 04:12 PM, Takashi Iwai wrote:
Do you by the way know of other, nicer and/or better patches than the supplied std and drums? Couldn't find any, at least not in the correct format it seems.
Hm, I don't know of others. It'd be fun to play with OPL3 parameter adjustment, though ;)
I was thinking of whether or not the standard patches shouldn't simple be builtin but anyone playing with an OPL3 these days is sort of by definition going for the geekness factor I guess. From that viewpoint, external patches do sound better...
[ README ]
- sbiload --opl3 std.o3 drums.o3
- % sbiload std.o3 drums.o3
Should the example indeed lose the --opl3?
With the patch in the previous mail, yes, it's needed. But my latest version that will be committed doesn't need --opl3 option. You can invoke like
% sbiload std drums
and the format and the extension will be chosen automatically according to the hwdep iface type.
Sounds great (assuming that I'll still be able to load the .sb patches into my OPL3 by just specifying std.sb and drums.sb ofcourse).
I believe that at that point you by the way might also make a simple "sbiload" enough, with the names defaulting to "std" and "drums"...
(and a -2 switch to pick std.sb and drums.sb even on an OPL3)
Rene.
Rene Herman wrote:
On 10/29/2007 04:12 PM, Takashi Iwai wrote:
Yes, I believe my OPL3 is back to its good ol' horrible self again with that version.
Here too.
Do you by the way know of other, nicer and/or better patches than the supplied std and drums? Couldn't find any, at least not in the correct format it seems.
I think I could poke around in some old Yamaha driver ...
Hm, I don't know of others. It'd be fun to play with OPL3 parameter adjustment, though ;)
I was thinking of whether or not the standard patches shouldn't simple be builtin but anyone playing with an OPL3 these days is sort of by definition going for the geekness factor I guess. From that viewpoint, external patches do sound better...
Trying to get General MIDI out of this chip doesn't do it justice.
Maybe I should put opledit into alsa-tools ...
Regards, Clemens
On 10/30/2007 09:24 AM, Clemens Ladisch wrote:
Do you by the way know of other, nicer and/or better patches than the supplied std and drums? Couldn't find any, at least not in the correct format it seems.
I think I could poke around in some old Yamaha driver ...
Wouldn't mind that...
Trying to get General MIDI out of this chip doesn't do it justice.
Maybe I should put opledit into alsa-tools ...
Nor that. I'm not all that familiar with the OPL (nor anything MIDI related in fact) but given my depository of ISA cards most of which have one, I sure do have a lot of them....
Rene.
At Tue, 30 Oct 2007 09:24:42 +0100, Clemens Ladisch wrote:
Hm, I don't know of others. It'd be fun to play with OPL3 parameter adjustment, though ;)
I was thinking of whether or not the standard patches shouldn't simple be builtin but anyone playing with an OPL3 these days is sort of by definition going for the geekness factor I guess. From that viewpoint, external patches do sound better...
Trying to get General MIDI out of this chip doesn't do it justice.
Maybe I should put opledit into alsa-tools ...
It's a good idea. Better to keep alive in the repo than leaving in wild.
Takashi
participants (3)
-
Clemens Ladisch
-
Rene Herman
-
Takashi Iwai