📄 lkeymidi.cpp
字号:
/* on OPL3, 0xC0 contains pan info, so don't set it until fm_key_on() */
if (midi_card != MIDI_OPL3)
lsFMWrite(0xC0+VOICE_OFFSET(voice), inst->feedback);
}
/* fm_set_drum_op1:
* Sets the sound for operator #1 of a drum channel.
*/
static inline void fm_set_drum_op1(int voice, FM_INSTRUMENT *inst)
{
lsFMWrite(0x20+fm_offset[voice], inst->characteristic1);
lsFMWrite(0x60+fm_offset[voice], inst->attackdecay1);
lsFMWrite(0x80+fm_offset[voice], inst->sustainrelease1);
lsFMWrite(0xE0+fm_offset[voice], inst->wave1);
}
/* fm_set_drum_op2:
* Sets the sound for operator #2 of a drum channel.
*/
static inline void fm_set_drum_op2(int voice, FM_INSTRUMENT *inst)
{
lsFMWrite(0x23+fm_offset[voice], inst->characteristic2);
lsFMWrite(0x63+fm_offset[voice], inst->attackdecay2);
lsFMWrite(0x83+fm_offset[voice], inst->sustainrelease2);
lsFMWrite(0xE3+fm_offset[voice], inst->wave2);
}
/* fm_set_drum_vol_op1:
* Sets the volume for operator #1 of a drum channel.
*/
static inline void fm_set_drum_vol_op1(int voice, int vol)
{
vol = 63 * fm_vol_table[vol] / 128;
vol = MIN((vol * lm_volume_step)>>2,63);
lsFMWrite(0x40+fm_offset[voice], (63-vol));
}
/* fm_set_drum_vol_op2:
* Sets the volume for operator #2 of a drum channel.
*/
static inline void fm_set_drum_vol_op2(int voice, int vol)
{
vol = 63 * fm_vol_table[vol] / 128;
vol = MIN((vol * lm_volume_step)>>2,63);
lsFMWrite(0x43+fm_offset[voice], (63-vol));
}
/* fm_set_drum_pitch:
* Sets the pitch of a drum channel.
*/
static inline void fm_set_drum_pitch(int voice, FM_INSTRUMENT *drum)
{
lsFMWrite(0xA0+VOICE_OFFSET(voice), drum->freq);
lsFMWrite(0xB0+VOICE_OFFSET(voice), drum->key & 0x1F);
}
/* fm_trigger_drum:
* Triggers a note on a drum channel.
*/
static inline void fm_trigger_drum(int inst, int vol)
{
FM_INSTRUMENT *drum = fm_drum+inst;
int d;
if (!fm_drum_mode)
fm_set_drum_mode(TRUE);
if (drum->type == FM_BD)
d = 0;
else if (drum->type == FM_SD)
d = 1;
else if (drum->type == FM_TT)
d = 2;
else if (drum->type == FM_CY)
d = 3;
else
d = 4;
/* don't let drum sounds come too close together */
if (fm_drum_cached_time[d] == _midi_tick)
return;
fm_drum_cached_time[d] = _midi_tick;
fm_drum_mask &= (~drum->type);
lsFMWrite(0xBD, fm_drum_mask);
vol = vol*3/4;
if (fm_drum_op1[d]) {
if (fm_drum_cached_inst1[d] != drum) {
fm_drum_cached_inst1[d] = drum;
fm_set_drum_op1(fm_drum_channel[d], drum);
}
if (fm_drum_cached_vol1[d] != vol) {
fm_drum_cached_vol1[d] = vol;
fm_set_drum_vol_op1(fm_drum_channel[d], vol);
}
}
if (fm_drum_op2[d]) {
if (fm_drum_cached_inst2[d] != drum) {
fm_drum_cached_inst2[d] = drum;
fm_set_drum_op2(fm_drum_channel[d], drum);
}
if (fm_drum_cached_vol2[d] != vol) {
fm_drum_cached_vol2[d] = vol;
fm_set_drum_vol_op2(fm_drum_channel[d], vol);
}
}
fm_set_drum_pitch(fm_drum_channel[d], drum);
fm_drum_mask |= drum->type;
lsFMWrite(0xBD, fm_drum_mask);
}
/* fm_key_on:
* Triggers the specified voice. The instrument is specified as a GM
* patch number, pitch as a midi note number, and volume from 0-127.
* The bend parameter is _not_ expressed as a midi pitch bend value.
* It ranges from 0 (no pitch change) to 0xFFF (almost a semitone sharp).
* Drum sounds are indicated by passing an instrument number greater than
* 128, in which case the sound is GM percussion key #(inst-128).
*/
int lm_midi_allocate_voice(int min,int max);
static void fm_key_on(int inst, int note, int bend, int vol, int pan)
{
int voice;
if (inst > 127) { /* drum sound? */
inst -= 163;
if (inst < 0)
inst = 0;
else if (inst > 46)
inst = 46;
fm_trigger_drum(inst, vol);
}
else { /* regular instrument */
if (midi_card == MIDI_2XOPL2) {
/* the SB Pro-1 has fixed pan positions per voice... */
if (pan < 64)
voice = lm_midi_allocate_voice(0, 5);
else
voice = lm_midi_allocate_voice(9, midi_driver->voices-1);
}
else
/* on other cards we can use any voices */
voice = lm_midi_allocate_voice(-1, -1);
if (voice < 0)
return;
/* make sure the voice isn't sounding */
lsFMWrite(0x43+fm_offset[voice], 63);
if (fm_feedback[voice] & 1)
lsFMWrite(0x40+fm_offset[voice], 63);
/* make sure the voice is set up with the right sound */
if (inst != fm_patch[voice]) {
fm_set_voice(voice, fm_instrument+inst);
fm_patch[voice] = inst;
}
/* set pan position */
if (midi_card == MIDI_OPL3) {
if (pan < 48)
pan = 0x10;
else if (pan >= 80)
pan = 0x20;
else
pan = 0x30;
lsFMWrite(0xC0+VOICE_OFFSET(voice), pan | fm_feedback[voice]);
}
/* and play the note */
fm_set_pitch(voice, note, bend);
fm_set_volume(voice, vol);
}
}
/* fm_key_off:
* Hey, guess what this does :-)
*/
static void fm_key_off(int voice)
{
lsFMWrite(0xB0+VOICE_OFFSET(voice), fm_key[voice] & 0xDF);
}
/* fm_set_volume:
* Sets the volume of the specified voice (vol range 0-127).
*/
static void fm_set_volume(int voice, int vol)
{
vol = fm_level[voice] * fm_vol_table[vol] / 128;
vol = MIN((vol*lm_volume_step)>>2,63); // by lin wei
lsFMWrite(0x43+fm_offset[voice], (63-vol) | fm_keyscale[voice]);
if (fm_feedback[voice] & 1)
lsFMWrite(0x40+fm_offset[voice], (63-vol) | fm_keyscale[voice]);
}
/* fm_set_pitch:
* Sets the pitch of the specified voice.
*/
static void fm_set_pitch(int voice, int note, int bend)
{
int oct = 1;
int freq;
note -= 24;
while (note >= 12) {
note -= 12;
oct++;
}
freq = fm_freq[note];
if (bend)
freq += (fm_freq[note+1] - fm_freq[note]) * bend / 0x1000;
fm_key[voice] = (oct<<2) | (freq >> 8);
lsFMWrite(0xA0+VOICE_OFFSET(voice), freq & 0xFF);
lsFMWrite(0xB0+VOICE_OFFSET(voice), fm_key[voice] | 0x20);
}
/* fm_load_patches:
* Called before starting to play a MIDI file, to check if we need to be
* in rhythm mode or not.
*/
static int fm_load_patches(char *patches, char *drums)
{
int i;
int usedrums = FALSE;
for (i=6; i<9; i++) {
fm_key[i] = 0;
fm_keyscale[i] = 0;
fm_feedback[i] = 0;
fm_level[i] = 0;
fm_patch[i] = -1;
lsFMWrite(0x40+fm_offset[i], 63);
lsFMWrite(0x43+fm_offset[i], 63);
}
for (i=0; i<5; i++) {
fm_drum_cached_inst1[i] = NULL;
fm_drum_cached_inst2[i] = NULL;
fm_drum_cached_vol1[i] = -1;
fm_drum_cached_vol2[i] = -1;
fm_drum_cached_time[i] = 0;
}
for (i=0; i<128; i++) {
if (drums[i]) {
usedrums = TRUE;
break;
}
}
fm_set_drum_mode(usedrums);
return 0;
}
/* fm_mixer_volume:
* For SB-Pro cards, sets the mixer volume for FM output.
*/
static int fm_mixer_volume(int volume)
{
return lsSetMixer(-1, volume);
}
/* fm_is_there:
* Checks for the presence of an OPL synth at the current port.
*/
static int fm_is_there()
{
lsFMWrite(1, 0); /* init test register */
lsFMWrite(4, 0x60); /* reset both timers */
lsFMWrite(4, 0x80); /* enable interrupts */
if (inp(lk_fm_port) & 0xE0)
return FALSE;
lsFMWrite(2, 0xFF); /* write 0xFF to timer 1 */
lsFMWrite(4, 0x21); /* start timer 1 */
delay(100);
if ((inp(lk_fm_port) & 0xE0) != 0xC0)
return FALSE;
lsFMWrite(4, 0x60); /* reset both timers */
lsFMWrite(4, 0x80); /* enable interrupts */
return TRUE;
}
/* fm_detect:
* Adlib detection routine.
*/
static int fm_detect()
{
static int ports[] =
{ 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x388, 0 };
int i;
char *s;
int opl_type;
if (lk_fm_port < 0||lk_sb_port < 0) {
if (midi_card == MIDI_OPL2||midi_card == MIDI_OPL3) {
lk_fm_port = 0x388;
if (fm_is_there())
goto found_it;
}
for (i=0; ports[i]; i++) { /* find the card */
lk_fm_port = ports[i];
if (fm_is_there())
goto found_it;
}
}
if (!fm_is_there()) {
strcpy(lm_error_msg, "OPL synth not found");
return FALSE;
}
found_it:
if ((inp(lk_fm_port) & 6) == 0) { /* check for OPL3 */
opl_type = MIDI_OPL3;
lsGetDspVersion();
}
else { /* check for second OPL2 */
if (lsGetDspVersion() >= 0x300)
opl_type = MIDI_2XOPL2;
else
opl_type = MIDI_OPL2;
}
if (midi_card == MIDI_OPL3) {
if (opl_type != MIDI_OPL3) {
strcpy(lm_error_msg, "OPL3 synth not found");
return FALSE;
}
}
else if (midi_card == MIDI_2XOPL2) {
if (opl_type != MIDI_2XOPL2) {
strcpy(lm_error_msg, "Second OPL2 synth not found");
return FALSE;
}
}
else if (midi_card != MIDI_OPL2)
midi_card = opl_type;
if (midi_card == MIDI_OPL2)
s = "OPL2 synth";
else if (midi_card == MIDI_2XOPL2)
s = "Dual OPL2 synths";
else
s = "OPL3 synth";
sprintf(adlib_desc, "%s on port %X", s, lk_fm_port);
midi_adlib.voices = (midi_card == MIDI_OPL2) ? 9 : 18;
midi_adlib.def_voices = midi_adlib.max_voices = midi_adlib.voices;
return TRUE;
}
/* load_ibk:
* Reads in a .IBK patch set file, for use by the Adlib driver.
*/
int lmFM_load_ibk(char *filename, int drums)
{
char sig[4];
FM_INSTRUMENT *inst;
int c, note, oct, skip, count;
FILE *f = fopen(filename, "rb");
if (!f)
return -1;
fread(sig, 4, 1, f);
if (memcmp(sig, "IBK\x1A", 4) != 0) {
fclose(f);
return -1;
}
if (drums) {
inst = fm_drum;
skip = 35;
count = 47;
}
else {
inst = fm_instrument;
skip = 0;
count = 128;
}
for (c=0; c<skip*16; c++)
fgetc(f);
for (c=0; c<count; c++) {
inst->characteristic1 = fgetc(f);
inst->characteristic2 = fgetc(f);
inst->level1 = fgetc(f);
inst->level2 = fgetc(f);
inst->attackdecay1 = fgetc(f);
inst->attackdecay2 = fgetc(f);
inst->sustainrelease1 = fgetc(f);
inst->sustainrelease2 = fgetc(f);
inst->wave1 = fgetc(f);
inst->wave2 = fgetc(f);
inst->feedback = fgetc(f);
if (drums) {
switch (fgetc(f)) {
case 6: inst->type = FM_BD; break;
case 7: inst->type = FM_HH; break;
case 8: inst->type = FM_TT; break;
case 9: inst->type = FM_SD; break;
case 10: inst->type = FM_CY; break;
default: inst->type = 0; break;
}
fgetc(f);
note = fgetc(f) - 24;
oct = 1;
while (note >= 12) {
note -= 12;
oct++;
}
inst->freq = fm_freq[note];
inst->key = (oct<<2) | (fm_freq[note] >> 8);
}
else {
inst->type = 0;
inst->freq = 0;
inst->key = 0;
fgetc(f);
fgetc(f);
fgetc(f);
}
fgetc(f);
fgetc(f);
inst++;
}
fclose(f);
return 0;
}
char *lmFM_read_inst_loc(int drums)
{ if (drums) return (char*)fm_drum;
return (char*)fm_instrument;
}
void lmFM_load_map(int choice,int drums)
{ FM_INSTRUMENT *inst,*source; int count,i;
if (drums) {
inst = fm_drum;
source = fm_drum_dat[choice];
count = 47;
}
else {
inst = fm_instrument;
source = fm_instrument_dat[choice];
count = 128;
}
for (i=0;i<count;i++,inst++,source++)
memcpy(inst,source,sizeof(FM_INSTRUMENT));
}
/* fm_init:
* Setup the adlib driver.
*/
static int fm_init(int voices)
{
char *s;
int i;
fm_reset(1);
lmFM_load_map(0,0); lmFM_load_map(0,1);
for (i=0; i<2; i++) {
if (i==0) s=lmMidiMapFile[0]; else s=lmMidiMapFile[1];
if ((s) && (s[0])) {
if (lmFM_load_ibk(s, (i > 0)) != 0) {
sprintf(lm_error_msg, "Error reading .IBK file '%s'", s);
return FALSE;
}
}
}
return TRUE;
}
static void fm_exit()
{
fm_reset(0);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -