// SPDX-License-Identifier: GPL-2.0-or-later /* * GM/GS/XG midi module. * * Copyright (C) 1999 Steve Ratcliffe * * Based on awe_wave.c by Takashi Iwai
*/ /* * This module is used to keep track of the current midi state. * It can be used for drivers that are required to emulate midi when * the hardware doesn't. * * It was written for a AWE64 driver, but there should be no AWE specific * code in here. If there is it should be reported as a bug.
*/
/* * Process an event in a driver independent way. This means dealing * with RPN, NRPN, SysEx etc that are defined for common midi applications * such as GM, GS and XG. * There modes that this module will run in are: * Generic MIDI - no interpretation at all, it will just save current values * of controllers etc. * GM - You can use all gm_ prefixed elements of chan. Controls, RPN, NRPN, * SysEx will be interpreded as defined in General Midi. * GS - You can use all gs_ prefixed elements of chan. Codes for GS will be * interpreted. * XG - You can use all xg_ prefixed elements of chan. Codes for XG will * be interpreted.
*/ void
snd_midi_process_event(conststruct snd_midi_op *ops, struct snd_seq_event *ev, struct snd_midi_channel_set *chanset)
{ struct snd_midi_channel *chan; void *drv; int dest_channel = 0;
if (ev == NULL || chanset == NULL) {
pr_debug("ALSA: seq_midi_emul: ev or chanbase NULL (snd_midi_process_event)\n"); return;
} if (chanset->channels == NULL) return;
if (snd_seq_ev_is_channel_type(ev)) {
dest_channel = ev->data.note.channel; if (dest_channel >= chanset->max_channels) {
pr_debug("ALSA: seq_midi_emul: dest channel is %d, max is %d\n",
dest_channel, chanset->max_channels); return;
}
}
/* EVENT_NOTE should be processed before queued */ if (ev->type == SNDRV_SEQ_EVENT_NOTE) return;
/* Make sure that we don't have a note on that should really be
* a note off */ if (ev->type == SNDRV_SEQ_EVENT_NOTEON && ev->data.note.velocity == 0)
ev->type = SNDRV_SEQ_EVENT_NOTEOFF;
/* Make sure the note is within array range */ if (ev->type == SNDRV_SEQ_EVENT_NOTEON ||
ev->type == SNDRV_SEQ_EVENT_NOTEOFF ||
ev->type == SNDRV_SEQ_EVENT_KEYPRESS) { if (ev->data.note.note >= 128) return;
}
switch (ev->type) { case SNDRV_SEQ_EVENT_NOTEON: if (chan->note[ev->data.note.note] & SNDRV_MIDI_NOTE_ON) { if (ops->note_off)
ops->note_off(drv, ev->data.note.note, 0, chan);
}
chan->note[ev->data.note.note] = SNDRV_MIDI_NOTE_ON; if (ops->note_on)
ops->note_on(drv, ev->data.note.note, ev->data.note.velocity, chan); break; case SNDRV_SEQ_EVENT_NOTEOFF: if (! (chan->note[ev->data.note.note] & SNDRV_MIDI_NOTE_ON)) break; if (ops->note_off)
note_off(ops, drv, chan, ev->data.note.note, ev->data.note.velocity); break; case SNDRV_SEQ_EVENT_KEYPRESS: if (ops->key_press)
ops->key_press(drv, ev->data.note.note, ev->data.note.velocity, chan); break; case SNDRV_SEQ_EVENT_CONTROLLER:
do_control(ops, drv, chanset, chan,
ev->data.control.param, ev->data.control.value); break; case SNDRV_SEQ_EVENT_PGMCHANGE:
chan->midi_program = ev->data.control.value; break; case SNDRV_SEQ_EVENT_PITCHBEND:
chan->midi_pitchbend = ev->data.control.value; if (ops->control)
ops->control(drv, MIDI_CTL_PITCHBEND, chan); break; case SNDRV_SEQ_EVENT_CHANPRESS:
chan->midi_pressure = ev->data.control.value; if (ops->control)
ops->control(drv, MIDI_CTL_CHAN_PRESSURE, chan); break; case SNDRV_SEQ_EVENT_CONTROL14: /* Best guess is that this is any of the 14 bit controller values */ if (ev->data.control.param < 32) { /* set low part first */
chan->control[ev->data.control.param + 32] =
ev->data.control.value & 0x7f;
do_control(ops, drv, chanset, chan,
ev->data.control.param,
((ev->data.control.value>>7) & 0x7f));
} else
do_control(ops, drv, chanset, chan,
ev->data.control.param,
ev->data.control.value); break; case SNDRV_SEQ_EVENT_NONREGPARAM: /* Break it back into its controller values */
chan->param_type = SNDRV_MIDI_PARAM_TYPE_NONREGISTERED;
chan->control[MIDI_CTL_MSB_DATA_ENTRY]
= (ev->data.control.value >> 7) & 0x7f;
chan->control[MIDI_CTL_LSB_DATA_ENTRY]
= ev->data.control.value & 0x7f;
chan->control[MIDI_CTL_NONREG_PARM_NUM_MSB]
= (ev->data.control.param >> 7) & 0x7f;
chan->control[MIDI_CTL_NONREG_PARM_NUM_LSB]
= ev->data.control.param & 0x7f;
nrpn(ops, drv, chan, chanset); break; case SNDRV_SEQ_EVENT_REGPARAM: /* Break it back into its controller values */
chan->param_type = SNDRV_MIDI_PARAM_TYPE_REGISTERED;
chan->control[MIDI_CTL_MSB_DATA_ENTRY]
= (ev->data.control.value >> 7) & 0x7f;
chan->control[MIDI_CTL_LSB_DATA_ENTRY]
= ev->data.control.value & 0x7f;
chan->control[MIDI_CTL_REGIST_PARM_NUM_MSB]
= (ev->data.control.param >> 7) & 0x7f;
chan->control[MIDI_CTL_REGIST_PARM_NUM_LSB]
= ev->data.control.param & 0x7f;
rpn(ops, drv, chan, chanset); break; case SNDRV_SEQ_EVENT_SYSEX: if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE) { unsignedchar sysexbuf[64]; int len;
len = snd_seq_expand_var_event(ev, sizeof(sysexbuf), sysexbuf, 1, 0); if (len > 0)
sysex(ops, drv, sysexbuf, len, chanset);
} break; case SNDRV_SEQ_EVENT_SONGPOS: case SNDRV_SEQ_EVENT_SONGSEL: case SNDRV_SEQ_EVENT_CLOCK: case SNDRV_SEQ_EVENT_START: case SNDRV_SEQ_EVENT_CONTINUE: case SNDRV_SEQ_EVENT_STOP: case SNDRV_SEQ_EVENT_QFRAME: case SNDRV_SEQ_EVENT_TEMPO: case SNDRV_SEQ_EVENT_TIMESIGN: case SNDRV_SEQ_EVENT_KEYSIGN: goto not_yet; case SNDRV_SEQ_EVENT_SENSING: break; case SNDRV_SEQ_EVENT_CLIENT_START: case SNDRV_SEQ_EVENT_CLIENT_EXIT: case SNDRV_SEQ_EVENT_CLIENT_CHANGE: case SNDRV_SEQ_EVENT_PORT_START: case SNDRV_SEQ_EVENT_PORT_EXIT: case SNDRV_SEQ_EVENT_PORT_CHANGE: case SNDRV_SEQ_EVENT_ECHO:
not_yet: default: /*pr_debug("ALSA: seq_midi_emul: Unimplemented event %d\n", ev->type);*/ break;
}
}
EXPORT_SYMBOL(snd_midi_process_event);
/* * release note
*/ staticvoid
note_off(conststruct snd_midi_op *ops, void *drv, struct snd_midi_channel *chan, int note, int vel)
{ if (chan->gm_hold) { /* Hold this note until pedal is turned off */
chan->note[note] |= SNDRV_MIDI_NOTE_RELEASED;
} elseif (chan->note[note] & SNDRV_MIDI_NOTE_SOSTENUTO) { /* Mark this note as release; it will be turned off when sostenuto
* is turned off */
chan->note[note] |= SNDRV_MIDI_NOTE_RELEASED;
} else {
chan->note[note] = 0; if (ops->note_off)
ops->note_off(drv, note, vel, chan);
}
}
/* * Do all driver independent operations for this controller and pass * events that need to take place immediately to the driver.
*/ staticvoid
do_control(conststruct snd_midi_op *ops, void *drv, struct snd_midi_channel_set *chset, struct snd_midi_channel *chan, int control, int value)
{ int i;
if (control >= ARRAY_SIZE(chan->control)) return;
/* Switches */ if ((control >=64 && control <=69) || (control >= 80 && control <= 83)) { /* These are all switches; either off or on so set to 0 or 127 */
value = (value >= 64)? 127: 0;
}
chan->control[control] = value;
switch (control) { case MIDI_CTL_SUSTAIN: if (value == 0) { /* Sustain has been released, turn off held notes */ for (i = 0; i < 128; i++) { if (chan->note[i] & SNDRV_MIDI_NOTE_RELEASED) {
chan->note[i] = SNDRV_MIDI_NOTE_OFF; if (ops->note_off)
ops->note_off(drv, i, 0, chan);
}
}
} break; case MIDI_CTL_PORTAMENTO: break; case MIDI_CTL_SOSTENUTO: if (value) { /* Mark each note that is currently held down */ for (i = 0; i < 128; i++) { if (chan->note[i] & SNDRV_MIDI_NOTE_ON)
chan->note[i] |= SNDRV_MIDI_NOTE_SOSTENUTO;
}
} else { /* release all notes that were held */ for (i = 0; i < 128; i++) { if (chan->note[i] & SNDRV_MIDI_NOTE_SOSTENUTO) {
chan->note[i] &= ~SNDRV_MIDI_NOTE_SOSTENUTO; if (chan->note[i] & SNDRV_MIDI_NOTE_RELEASED) {
chan->note[i] = SNDRV_MIDI_NOTE_OFF; if (ops->note_off)
ops->note_off(drv, i, 0, chan);
}
}
}
} break; case MIDI_CTL_MSB_DATA_ENTRY:
chan->control[MIDI_CTL_LSB_DATA_ENTRY] = 0;
fallthrough; case MIDI_CTL_LSB_DATA_ENTRY: if (chan->param_type == SNDRV_MIDI_PARAM_TYPE_REGISTERED)
rpn(ops, drv, chan, chset); else
nrpn(ops, drv, chan, chset); break; case MIDI_CTL_REGIST_PARM_NUM_LSB: case MIDI_CTL_REGIST_PARM_NUM_MSB:
chan->param_type = SNDRV_MIDI_PARAM_TYPE_REGISTERED; break; case MIDI_CTL_NONREG_PARM_NUM_LSB: case MIDI_CTL_NONREG_PARM_NUM_MSB:
chan->param_type = SNDRV_MIDI_PARAM_TYPE_NONREGISTERED; break;
case MIDI_CTL_ALL_SOUNDS_OFF:
all_sounds_off(ops, drv, chan); break;
case MIDI_CTL_ALL_NOTES_OFF:
all_notes_off(ops, drv, chan); break;
case MIDI_CTL_MSB_BANK: if (chset->midi_mode == SNDRV_MIDI_MODE_XG) { if (value == 127)
chan->drum_channel = 1; else
chan->drum_channel = 0;
} break; case MIDI_CTL_LSB_BANK: break;
case MIDI_CTL_RESET_CONTROLLERS:
snd_midi_reset_controllers(chan); break;
case MIDI_CTL_SOFT_PEDAL: case MIDI_CTL_LEGATO_FOOTSWITCH: case MIDI_CTL_HOLD2: case MIDI_CTL_SC1_SOUND_VARIATION: case MIDI_CTL_SC2_TIMBRE: case MIDI_CTL_SC3_RELEASE_TIME: case MIDI_CTL_SC4_ATTACK_TIME: case MIDI_CTL_SC5_BRIGHTNESS: case MIDI_CTL_E1_REVERB_DEPTH: case MIDI_CTL_E2_TREMOLO_DEPTH: case MIDI_CTL_E3_CHORUS_DEPTH: case MIDI_CTL_E4_DETUNE_DEPTH: case MIDI_CTL_E5_PHASER_DEPTH: goto notyet;
notyet: default: if (ops->control)
ops->control(drv, control, chan); break;
}
}
/* * initialize the MIDI status
*/ void
snd_midi_channel_set_clear(struct snd_midi_channel_set *chset)
{ int i;
if (n == 9)
p->drum_channel = 1; /* Default ch 10 as drums */
}
/* * Allocate and initialise a set of midi channel control blocks.
*/ staticstruct snd_midi_channel *snd_midi_channel_init_set(int n)
{ struct snd_midi_channel *chan; int i;
chan = kmalloc_array(n, sizeof(struct snd_midi_channel), GFP_KERNEL); if (chan) { for (i = 0; i < n; i++)
snd_midi_channel_init(chan+i, i);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.