📄 opl3_midi.c
字号:
snd_printk(" --> setting OPL3 connection: 0x%x\n", opl3->connection_reg);#endif /* * calculate volume depending on connection * between FM operators (see include/opl3.h) */ for (i = 0; i < (instr_4op ? 4 : 2); i++) vol_op[i] = fm->op[i].ksl_level; connection = fm->feedback_connection[0] & 0x01; if (instr_4op) { connection <<= 1; connection |= fm->feedback_connection[1] & 0x01; snd_opl3_calc_volume(&vol_op[3], vel, chan); switch (connection) { case 0x03: snd_opl3_calc_volume(&vol_op[2], vel, chan); /* fallthru */ case 0x02: snd_opl3_calc_volume(&vol_op[0], vel, chan); break; case 0x01: snd_opl3_calc_volume(&vol_op[1], vel, chan); } } else { snd_opl3_calc_volume(&vol_op[1], vel, chan); if (connection) snd_opl3_calc_volume(&vol_op[0], vel, chan); } /* Program the FM voice characteristics */ for (i = 0; i < (instr_4op ? 4 : 2); i++) {#ifdef DEBUG_MIDI snd_printk(" --> programming operator %i\n", i);#endif op_offset = snd_opl3_regmap[voice_offset][i]; /* Set OPL3 AM_VIB register of requested voice/operator */ reg_val = fm->op[i].am_vib; opl3_reg = reg_side | (OPL3_REG_AM_VIB + op_offset); opl3->command(opl3, opl3_reg, reg_val); /* Set OPL3 KSL_LEVEL register of requested voice/operator */ reg_val = vol_op[i]; opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + op_offset); opl3->command(opl3, opl3_reg, reg_val); /* Set OPL3 ATTACK_DECAY register of requested voice/operator */ reg_val = fm->op[i].attack_decay; opl3_reg = reg_side | (OPL3_REG_ATTACK_DECAY + op_offset); opl3->command(opl3, opl3_reg, reg_val); /* Set OPL3 SUSTAIN_RELEASE register of requested voice/operator */ reg_val = fm->op[i].sustain_release; opl3_reg = reg_side | (OPL3_REG_SUSTAIN_RELEASE + op_offset); opl3->command(opl3, opl3_reg, reg_val); /* Select waveform */ reg_val = fm->op[i].wave_select; opl3_reg = reg_side | (OPL3_REG_WAVE_SELECT + op_offset); opl3->command(opl3, opl3_reg, reg_val); } /* Set operator feedback and 2op inter-operator connection */ reg_val = fm->feedback_connection[0]; /* Set output voice connection */ reg_val |= OPL3_STEREO_BITS; if (chan->gm_pan < 43) reg_val &= ~OPL3_VOICE_TO_RIGHT; if (chan->gm_pan > 85) reg_val &= ~OPL3_VOICE_TO_LEFT; opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset); opl3->command(opl3, opl3_reg, reg_val); if (instr_4op) { /* Set 4op inter-operator connection */ reg_val = fm->feedback_connection[1] & OPL3_CONNECTION_BIT; /* Set output voice connection */ reg_val |= OPL3_STEREO_BITS; if (chan->gm_pan < 43) reg_val &= ~OPL3_VOICE_TO_RIGHT; if (chan->gm_pan > 85) reg_val &= ~OPL3_VOICE_TO_LEFT; opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset + 3); opl3->command(opl3, opl3_reg, reg_val); } /* * Special treatment of percussion notes for fm: * Requested pitch is really program, and pitch for * device is whatever was specified in the patch library. */ if (fm->fix_key) note = fm->fix_key; /* * use transpose if defined in patch library */ if (fm->trnsps) note += (fm->trnsps - 64); snd_opl3_calc_pitch(&fnum, &blocknum, note, chan); /* Set OPL3 FNUM_LOW register of requested voice */ opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset); opl3->command(opl3, opl3_reg, fnum); opl3->voices[voice].keyon_reg = blocknum; /* Set output sound flag */ blocknum |= OPL3_KEYON_BIT;#ifdef DEBUG_MIDI snd_printk(" --> trigger voice %i\n", voice);#endif /* Set OPL3 KEYON_BLOCK register of requested voice */ opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); opl3->command(opl3, opl3_reg, blocknum); /* kill note after fixed duration (in centiseconds) */ if (fm->fix_dur) { opl3->voices[voice].note_off = jiffies + (fm->fix_dur * HZ) / 100; snd_opl3_start_timer(opl3); opl3->voices[voice].note_off_check = 1; } else opl3->voices[voice].note_off_check = 0; /* 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; vp->chan = chan; if (instr_4op) { vp->state = SNDRV_OPL3_ST_ON_4OP; vp2 = &opl3->voices[voice + 3]; vp2->time = opl3->use_time++; vp2->note = key; vp2->chan = chan; vp2->state = SNDRV_OPL3_ST_NOT_AVAIL; } else { if (vp->state == SNDRV_OPL3_ST_ON_4OP) { /* 4op killed by 2op, release bounded voice */ vp2 = &opl3->voices[voice + 3]; vp2->time = opl3->use_time++; vp2->state = SNDRV_OPL3_ST_OFF; } vp->state = SNDRV_OPL3_ST_ON_2OP; }#ifdef DEBUG_ALLOC debug_alloc(opl3, "note on ", voice);#endif /* allocate extra program if specified in patch library */ if (extra_prg) { if (extra_prg > 128) { wanted.bank = 128; /* percussions start at 35 */ wanted.prg = extra_prg - 128 + 35 - 1; } else { wanted.bank = 0; wanted.prg = extra_prg - 1; }#ifdef DEBUG_MIDI snd_printk(" *** allocating extra program\n");#endif goto __extra_prg; } spin_unlock_irqrestore(&opl3->voice_lock, flags);}static void snd_opl3_kill_voice(opl3_t *opl3, int voice){ unsigned short reg_side; unsigned char voice_offset; unsigned short opl3_reg; snd_opl3_voice_t *vp, *vp2; snd_assert(voice < MAX_OPL3_VOICES, return); vp = &opl3->voices[voice]; if (voice < MAX_OPL2_VOICES) { /* Left register block for voices 0 .. 8 */ reg_side = OPL3_LEFT; voice_offset = voice; } else { /* Right register block for voices 9 .. 17 */ reg_side = OPL3_RIGHT; voice_offset = voice - MAX_OPL2_VOICES; } /* kill voice */#ifdef DEBUG_MIDI snd_printk(" --> kill voice %i\n", voice);#endif opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); /* clear Key ON bit */ opl3->command(opl3, opl3_reg, vp->keyon_reg); /* do the bookkeeping */ vp->time = opl3->use_time++; if (vp->state == SNDRV_OPL3_ST_ON_4OP) { vp2 = &opl3->voices[voice + 3]; vp2->time = opl3->use_time++; vp2->state = SNDRV_OPL3_ST_OFF; } vp->state = SNDRV_OPL3_ST_OFF;#ifdef DEBUG_ALLOC debug_alloc(opl3, "note off", voice);#endif}/* * Release a note in response to a midi note off. */void snd_opl3_note_off(void *p, int note, int vel, snd_midi_channel_t *chan){ opl3_t *opl3; int voice; snd_opl3_voice_t *vp; unsigned long flags; opl3 = p;#ifdef DEBUG_MIDI snd_printk("Note off, ch %i, inst %i, note %i\n", chan->number, chan->midi_program, note);#endif spin_lock_irqsave(&opl3->voice_lock, flags); if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { if (chan->drum_channel && use_internal_drums) { snd_opl3_drum_switch(opl3, note, vel, 0, chan); spin_unlock_irqrestore(&opl3->voice_lock, flags); return; } /* this loop will hopefully kill all extra voices, because they are grouped by the same channel and note values */ for (voice = 0; voice < opl3->max_voices; voice++) { vp = &opl3->voices[voice]; if (vp->state > 0 && vp->chan == chan && vp->note == note) { snd_opl3_kill_voice(opl3, voice); } } } else { /* remap OSS voices */ if (chan->number < MAX_OPL3_VOICES) { voice = snd_opl3_oss_map[chan->number]; snd_opl3_kill_voice(opl3, voice); } } spin_unlock_irqrestore(&opl3->voice_lock, flags);}/* * key pressure change */void snd_opl3_key_press(void *p, int note, int vel, snd_midi_channel_t *chan){ opl3_t *opl3; opl3 = p;#ifdef DEBUG_MIDI snd_printk("Key pressure, ch#: %i, inst#: %i\n", chan->number, chan->midi_program);#endif}/* * terminate note */void snd_opl3_terminate_note(void *p, int note, snd_midi_channel_t *chan){ opl3_t *opl3; opl3 = p;#ifdef DEBUG_MIDI snd_printk("Terminate note, ch#: %i, inst#: %i\n", chan->number, chan->midi_program);#endif}static void snd_opl3_update_pitch(opl3_t *opl3, int voice){ unsigned short reg_side; unsigned char voice_offset; unsigned short opl3_reg; unsigned char fnum, blocknum; snd_opl3_voice_t *vp; snd_assert(voice < MAX_OPL3_VOICES, return); vp = &opl3->voices[voice]; if (vp->chan == NULL) return; /* not allocated? */ if (voice < MAX_OPL2_VOICES) { /* Left register block for voices 0 .. 8 */ reg_side = OPL3_LEFT; voice_offset = voice; } else { /* Right register block for voices 9 .. 17 */ reg_side = OPL3_RIGHT; voice_offset = voice - MAX_OPL2_VOICES; } snd_opl3_calc_pitch(&fnum, &blocknum, vp->note, vp->chan); /* Set OPL3 FNUM_LOW register of requested voice */ opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset); opl3->command(opl3, opl3_reg, fnum); vp->keyon_reg = blocknum; /* Set output sound flag */ blocknum |= OPL3_KEYON_BIT; /* Set OPL3 KEYON_BLOCK register of requested voice */ opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); opl3->command(opl3, opl3_reg, blocknum); vp->time = opl3->use_time++;}/* * Update voice pitch controller */static void snd_opl3_pitch_ctrl(opl3_t *opl3, snd_midi_channel_t *chan){ int voice; snd_opl3_voice_t *vp; unsigned long flags; spin_lock_irqsave(&opl3->voice_lock, flags); if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { for (voice = 0; voice < opl3->max_voices; voice++) { vp = &opl3->voices[voice]; if (vp->state > 0 && vp->chan == chan) { snd_opl3_update_pitch(opl3, voice); } } } else { /* remap OSS voices */ if (chan->number < MAX_OPL3_VOICES) { voice = snd_opl3_oss_map[chan->number]; snd_opl3_update_pitch(opl3, voice); } } spin_unlock_irqrestore(&opl3->voice_lock, flags);}/* * Deal with a controler type event. This includes all types of * control events, not just the midi controllers */void snd_opl3_control(void *p, int type, snd_midi_channel_t *chan){ opl3_t *opl3; opl3 = p;#ifdef DEBUG_MIDI snd_printk("Controller, TYPE = %i, ch#: %i, inst#: %i\n", type, chan->number, chan->midi_program);#endif switch (type) { case MIDI_CTL_MSB_MODWHEEL: if (chan->control[MIDI_CTL_MSB_MODWHEEL] > 63) opl3->drum_reg |= OPL3_VIBRATO_DEPTH; else opl3->drum_reg &= ~OPL3_VIBRATO_DEPTH; opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, opl3->drum_reg); break; case MIDI_CTL_E2_TREMOLO_DEPTH: if (chan->control[MIDI_CTL_E2_TREMOLO_DEPTH] > 63) opl3->drum_reg |= OPL3_TREMOLO_DEPTH; else opl3->drum_reg &= ~OPL3_TREMOLO_DEPTH; opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, opl3->drum_reg); break; case MIDI_CTL_PITCHBEND: snd_opl3_pitch_ctrl(opl3, chan); break; }}/* * NRPN events */void snd_opl3_nrpn(void *p, snd_midi_channel_t *chan, snd_midi_channel_set_t *chset){ opl3_t *opl3; opl3 = p;#ifdef DEBUG_MIDI snd_printk("NRPN, ch#: %i, inst#: %i\n", chan->number, chan->midi_program);#endif}/* * receive sysex */void snd_opl3_sysex(void *p, unsigned char *buf, int len, int parsed, snd_midi_channel_set_t *chset){ opl3_t *opl3; opl3 = p;#ifdef DEBUG_MIDI snd_printk("SYSEX\n");#endif}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -