📄 opl3.c
字号:
if (map->voice_mode == 2 && instr->key == OPL3_PATCH) return 0; /* Cannot play */ voice_mode = map->voice_mode; if (voice_mode == 4) { int voice_shift; voice_shift = (map->ioaddr == left_address) ? 0 : 3; voice_shift += map->voice_num; if (instr->key != OPL3_PATCH) /* Just 2 OP patch */ { voice_mode = 2; connection_mask &= ~(1 << voice_shift); } else { connection_mask |= (1 << voice_shift); } opl3_command (right_address, CONNECTION_SELECT_REGISTER, connection_mask); } /* Set Sound Characteristics */ opl3_command (map->ioaddr, AM_VIB + map->op[0], instr->operators[0]); opl3_command (map->ioaddr, AM_VIB + map->op[1], instr->operators[1]); /* Set Attack/Decay */ opl3_command (map->ioaddr, ATTACK_DECAY + map->op[0], instr->operators[4]); opl3_command (map->ioaddr, ATTACK_DECAY + map->op[1], instr->operators[5]); /* Set Sustain/Release */ opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[0], instr->operators[6]); opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[1], instr->operators[7]); /* Set Wave Select */ opl3_command (map->ioaddr, WAVE_SELECT + map->op[0], instr->operators[8]); opl3_command (map->ioaddr, WAVE_SELECT + map->op[1], instr->operators[9]); /* Set Feedback/Connection */ fpc = instr->operators[10]; if (!(fpc & 0x30)) fpc |= 0x30; /* Ensure that at least one chn is enabled */ opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num, fpc); /* * If the voice is a 4 OP one, initialize the operators 3 and 4 also */ if (voice_mode == 4) { /* Set Sound Characteristics */ opl3_command (map->ioaddr, AM_VIB + map->op[2], instr->operators[OFFS_4OP + 0]); opl3_command (map->ioaddr, AM_VIB + map->op[3], instr->operators[OFFS_4OP + 1]); /* Set Attack/Decay */ opl3_command (map->ioaddr, ATTACK_DECAY + map->op[2], instr->operators[OFFS_4OP + 4]); opl3_command (map->ioaddr, ATTACK_DECAY + map->op[3], instr->operators[OFFS_4OP + 5]); /* Set Sustain/Release */ opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[2], instr->operators[OFFS_4OP + 6]); opl3_command (map->ioaddr, SUSTAIN_RELEASE + map->op[3], instr->operators[OFFS_4OP + 7]); /* Set Wave Select */ opl3_command (map->ioaddr, WAVE_SELECT + map->op[2], instr->operators[OFFS_4OP + 8]); opl3_command (map->ioaddr, WAVE_SELECT + map->op[3], instr->operators[OFFS_4OP + 9]); /* Set Feedback/Connection */ fpc = instr->operators[OFFS_4OP + 10]; if (!(fpc & 0x30)) fpc |= 0x30; /* Ensure that at least one chn is enabled */ opl3_command (map->ioaddr, FEEDBACK_CONNECTION + map->voice_num + 3, fpc); } voices[voice].mode = voice_mode; set_voice_volume (voice, volume); freq = voices[voice].orig_freq = note_to_freq (note) / 1000; /* * Since the pitch bender may have been set before playing the note, we * have to calculate the bending now. */ freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range); voices[voice].current_freq = freq; freq_to_fnum (freq, &block, &fnum); /* Play note */ data = fnum & 0xff; /* Least significant bits of fnumber */ opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data); data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); voices[voice].keyon_byte = data; opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data); if (voice_mode == 4) opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data); return 0;}static voidfreq_to_fnum (int freq, int *block, int *fnum){ int f, octave; /* Converts the note frequency to block and fnum values for the FM chip */ /* First try to compute the block -value (octave) where the note belongs */ f = freq; octave = 5; if (f == 0) octave = 0; else if (f < 261) { while (f < 261) { octave--; f <<= 1; } } else if (f > 493) { while (f > 493) { octave++; f >>= 1; } } if (octave > 7) octave = 7; *fnum = freq * (1 << (20 - octave)) / 49716; *block = octave;}static voidopl3_command (int io_addr, unsigned int addr, unsigned int val){ int i; /* * The original 2-OP synth requires a quite long delay after writing to a * register. The OPL-3 survives with just two INBs */ OUTB ((unsigned char)(addr & 0xff), io_addr); /* Select register */ if (!opl3_enabled) tenmicrosec (); else for (i = 0; i < 2; i++) INB (io_addr); OUTB ((unsigned char)(val & 0xff), io_addr + 1); /* Write to register */ if (!opl3_enabled) { tenmicrosec (); tenmicrosec (); tenmicrosec (); } else for (i = 0; i < 2; i++) INB (io_addr);}static voidopl3_reset (int dev){ int i; for (i = 0; i < nr_voices; i++) { opl3_command (physical_voices[logical_voices[i]].ioaddr, KSL_LEVEL + physical_voices[logical_voices[i]].op[0], 0xff); /* OP1 volume to min */ opl3_command (physical_voices[logical_voices[i]].ioaddr, KSL_LEVEL + physical_voices[logical_voices[i]].op[1], 0xff); /* OP2 volume to min */ if (physical_voices[logical_voices[i]].voice_mode == 4) /* 4 OP voice */ { opl3_command (physical_voices[logical_voices[i]].ioaddr, KSL_LEVEL + physical_voices[logical_voices[i]].op[2], 0xff); /* OP3 volume to min */ opl3_command (physical_voices[logical_voices[i]].ioaddr, KSL_LEVEL + physical_voices[logical_voices[i]].op[3], 0xff); /* OP4 volume to min */ } opl3_kill_note (dev, i, 64); } if (opl3_enabled) { nr_voices = 18; for (i = 0; i < 18; i++) logical_voices[i] = i; for (i = 0; i < 18; i++) physical_voices[i].voice_mode = 2; }}static intopl3_open (int dev, int mode){ if (!opl3_ok) return RET_ERROR (ENXIO); if (opl3_busy) return RET_ERROR (EBUSY); opl3_busy = 1; connection_mask = 0x00; /* Just 2 OP voices */ if (opl3_enabled) opl3_command (right_address, CONNECTION_SELECT_REGISTER, connection_mask); return 0;}static voidopl3_close (int dev){ opl3_busy = 0; nr_voices = opl3_enabled ? 18 : 9; fm_info.nr_drums = 0; fm_info.perc_mode = 0; opl3_reset (dev);}static voidopl3_hw_control (int dev, unsigned char *event){}static intopl3_load_patch (int dev, int format, snd_rw_buf * addr, int offs, int count, int pmgr_flag){ struct sbi_instrument ins; if (count < sizeof (ins)) { printk ("FM Error: Patch record too short\n"); return RET_ERROR (EINVAL); } COPY_FROM_USER (&((char *) &ins)[offs], (char *) addr, offs, sizeof (ins) - offs); if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) { printk ("FM Error: Invalid instrument number %d\n", ins.channel); return RET_ERROR (EINVAL); } ins.key = format; return store_instr (ins.channel, &ins);}static voidopl3_panning (int dev, int voice, int pressure){}#define SET_VIBRATO(cell) { \ tmp = instr->operators[(cell-1)+(((cell-1)/2)*OFFS_4OP)]; \ if (pressure > 110) \ tmp |= 0x40; /* Vibrato on */ \ opl3_command (map->ioaddr, AM_VIB + map->op[cell-1], tmp);}static voidopl3_aftertouch (int dev, int voice, int pressure){ int tmp; struct sbi_instrument *instr; struct physical_voice_info *map; if (voice < 0 || voice >= nr_voices) return; map = &physical_voices[logical_voices[voice]]; DEB (printk ("Aftertouch %d\n", voice)); if (map->voice_mode == 0) return; /* * Adjust the amount of vibrato depending the pressure */ instr = active_instrument[voice]; if (!instr) instr = &instrmap[0]; if (voices[voice].mode == 4) { int connection = ((instr->operators[10] & 0x01) << 1) | (instr->operators[10 + OFFS_4OP] & 0x01); switch (connection) { case 0: SET_VIBRATO (4); break; case 1: SET_VIBRATO (2); SET_VIBRATO (4); break; case 2: SET_VIBRATO (1); SET_VIBRATO (4); break; case 3: SET_VIBRATO (1); SET_VIBRATO (3); SET_VIBRATO (4); break; } /* Not implemented yet */ } else { SET_VIBRATO (1); if ((instr->operators[10] & 0x01)) /* Additive synthesis */ SET_VIBRATO (2); }}#undef SET_VIBRATOstatic voidopl3_controller (int dev, int voice, int ctrl_num, int value){ unsigned char data; int block, fnum, freq; struct physical_voice_info *map; if (voice < 0 || voice >= nr_voices) return; map = &physical_voices[logical_voices[voice]]; if (map->voice_mode == 0) return; switch (ctrl_num) { case CTRL_PITCH_BENDER: voices[voice].bender = value; if (!value) return; if (!(voices[voice].keyon_byte & 0x20)) return; /* Not keyed on */ freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender, voices[voice].bender_range); voices[voice].current_freq = freq; freq_to_fnum (freq, &block, &fnum); data = fnum & 0xff; /* Least significant bits of fnumber */ opl3_command (map->ioaddr, FNUM_LOW + map->voice_num, data); data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); /* KEYON|OCTAVE|MS bits * of f-num */ voices[voice].keyon_byte = data; opl3_command (map->ioaddr, KEYON_BLOCK + map->voice_num, data); break; case CTRL_PITCH_BENDER_RANGE: voices[voice].bender_range = value; break; }}static intopl3_patchmgr (int dev, struct patmgr_info *rec){ return RET_ERROR (EINVAL);}static struct synth_operations opl3_operations ={ &fm_info, SYNTH_TYPE_FM, FM_TYPE_ADLIB, opl3_open, opl3_close, opl3_ioctl, opl3_kill_note, opl3_start_note, opl3_set_instr, opl3_reset, opl3_hw_control, opl3_load_patch, opl3_aftertouch, opl3_controller, opl3_panning, opl3_patchmgr};longopl3_init (long mem_start){ int i; PERMANENT_MALLOC(struct sbi_instrument*, instrmap, SBFM_MAXINSTR*sizeof(*instrmap), mem_start); synth_devs[num_synths++] = &opl3_operations; fm_model = 0; opl3_ok = 1; if (opl3_enabled) { printk (" <Yamaha OPL-3 FM>"); fm_model = 2; nr_voices = 18; fm_info.nr_drums = 0; fm_info.capabilities |= SYNTH_CAP_OPL3;#ifndef SCO strcpy (fm_info.name, "Yamaha OPL-3");#endif for (i = 0; i < 18; i++) if (physical_voices[i].ioaddr == USE_LEFT) physical_voices[i].ioaddr = left_address; else physical_voices[i].ioaddr = right_address; opl3_command (right_address, OPL3_MODE_REGISTER, OPL3_ENABLE); /* Enable OPL-3 mode */ opl3_command (right_address, CONNECTION_SELECT_REGISTER, 0x00); /* Select all 2-OP * voices */ } else { printk (" <Yamaha 2-OP FM>"); fm_model = 1; nr_voices = 9; fm_info.nr_drums = 0; for (i = 0; i < 18; i++) physical_voices[i].ioaddr = left_address; }; already_initialized = 1; for (i = 0; i < SBFM_MAXINSTR; i++) instrmap[i].channel = -1; return mem_start;}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -