📄 midi.h
字号:
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;
FMWrite(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 (aost 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 _midi_allocate_voice(int min,int max);
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 = _midi_allocate_voice(0, 5);
else
voice = _midi_allocate_voice(9, midi_driver->voices-1);
}
else
/* on other cards we can use any voices */
voice = _midi_allocate_voice(-1, -1);
if (voice < 0)
return;
/* make sure the voice isn't sounding */
FMWrite(0x43+fm_offset[voice], 63);
if (fm_feedback[voice] & 1)
FMWrite(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;
FMWrite(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 :-)
*/
void fm_key_off(int voice)
{
FMWrite(0xB0+VOICE_OFFSET(voice), fm_key[voice] & 0xDF);
}
/* fm_set_volume:
* Sets the volume of the specified voice (vol range 0-127).
*/
void fm_set_volume(int voice, int vol)
{
vol = fm_level[voice] * fm_vol_table[vol] / 128;
vol = MIN((vol*_volume_step)>>2,63); // by lin wei
FMWrite(0x43+fm_offset[voice], (63-vol) | fm_keyscale[voice]);
if (fm_feedback[voice] & 1)
FMWrite(0x40+fm_offset[voice], (63-vol) | fm_keyscale[voice]);
}
/* fm_set_pitch:
* Sets the pitch of the specified voice.
*/
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);
FMWrite(0xA0+VOICE_OFFSET(voice), freq & 0xFF);
FMWrite(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.
*/
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;
FMWrite(0x40+fm_offset[i], 63);
FMWrite(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.
*/
int fm_mixer_volume(int volume)
{
return SetMixer(-1, volume);
}
/* fm_is_there:
* Checks for the presence of an OPL synth at the current port.
*/
int fm_is_there()
{
FMWrite(1, 0); /* init test register */
FMWrite(4, 0x60); /* reset both timers */
FMWrite(4, 0x80); /* enable interrupts */
if (inp(lk_fm_port) & 0xE0)
return FALSE;
FMWrite(2, 0xFF); /* write 0xFF to timer 1 */
FMWrite(4, 0x21); /* start timer 1 */
delay(100);
if ((inp(lk_fm_port) & 0xE0) != 0xC0)
return FALSE;
FMWrite(4, 0x60); /* reset both timers */
FMWrite(4, 0x80); /* enable interrupts */
return TRUE;
}
/* fm_detect:
* Adlib detection routine.
*/
int fm_detect()
{
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()) {
sprintf(grp_err,"OPL synth not found");
return FALSE;
}
found_it:
if ((inp(lk_fm_port) & 6) == 0) { /* check for OPL3 */
opl_type = MIDI_OPL3;
GetDspVersion();
}
else { /* check for second OPL2 */
if (GetDspVersion() >= 0x300)
opl_type = MIDI_2XOPL2;
else
opl_type = MIDI_OPL2;
}
if (midi_card == MIDI_OPL3) {
if (opl_type != MIDI_OPL3) {
sprintf(grp_err,"OPL3 synth not found");
return FALSE;
}
}
else if (midi_card == MIDI_2XOPL2) {
if (opl_type != MIDI_2XOPL2) {
sprintf(grp_err,"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 FM_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 *FM_read_inst_loc(int drums)
{ if (drums) return (char*)fm_drum;
return (char*)fm_instrument;
}
void FM_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.
*/
int fm_init(int voices)
{
char *s;
int i;
fm_reset(1);
FM_load_map(0,0); FM_load_map(0,1);
for (i=0; i<2; i++) {
if (i==0) s=MidiMapFile[0]; else s=MidiMapFile[1];
if ((s) && (s[0])) {
if (FM_load_ibk(s, (i > 0)) != 0) {
sprintf(grp_err,"Error reading .IBK file '%s'", s);
return FALSE;
}
}
}
return TRUE;
}
void fm_exit()
{
fm_reset(0);
}
char gWritePortC(unsigned char value)
{ int i;
for (i=0;i<0xffff ;i++ )
{ if (!(inp(0x0C+lk_sb_port) & 0x80)) {
outp(lk_sb_port+0x0c,value);
return 0;
}
} return -1;
}
unsigned char gReadPortC()
{ short b;
for (b=50;b>0&&inp(lk_sb_port+0x0e)<128;b--);
return inp(lk_sb_port+0x0a);
}
short GetDspVersion()
{ short c;
if (lk_dsp_ver>0) return lk_dsp_ver;
gWritePortC(0xe1); c=gReadPortC();
lk_dsp_ver=((unsigned)c<<8) + gReadPortC();
return lk_dsp_ver;
}
void MixerWrite(short reg,short data)
{ outp(lk_sb_port+4, reg);
outp(lk_sb_port+5, data);
}
short MixerRead(short reg)
{ outp(lk_sb_port+4, reg);
return inp(lk_sb_port+5);
}
short SetMixer(int digi_volume, int midi_volume)
{ short _sb_port=lk_sb_port;
if (GetDspVersion() < 0x300) return -1;
if (digi_volume >= 0) {
outp(_sb_port+4, 4);
outp(_sb_port+5, (digi_volume & 0xF0) | (digi_volume >> 4));
}
if (midi_volume >= 0) {
outp(_sb_port+4, 0x26);
outp(_sb_port+5, (midi_volume & 0xF0) | (midi_volume >> 4));
}
return 0;
}
///////////////////////////////////////////////
void FMWrite(unsigned short reg, unsigned char data)
{
short i;
short port = (reg & 0x100) ? lk_fm_port+2 : lk_fm_port;
outp(port, reg & 0xFF);
for (i=0; i<lk_fm_delay1; i++) inp(port);
outp(port+1, data);
for (i=0; i<lk_fm_delay2; i++) inp(port);
}
unsigned char FMRead()
{ return (inp(lk_fm_port)); }
void FMInit()
{ FMWrite(1,0);
FMWrite(8,0);
FMWrite(0xbd,0x00);
FMWrite(0x20+0,0x21);
FMWrite(0x20+3,0x11);
FMWrite(0x40+0,0x4c);
FMWrite(0x40+3,0x00);
FMWrite(0x60+0,0xd2);
FMWrite(0x60+3,0xd2);
FMWrite(0x80+0,0x32);
FMWrite(0x80+3,0x11);
FMWrite(0xe0+0,0x00);
FMWrite(0xe0+3,0x00);
FMWrite(0xc0+0,0x04);
FMWrite(0x01,0x20); /* turn on wave form control */
FMWrite(0xBD, 0x0c); /* set AM and vibrato to high */
}
/////////////////////////////////////
void FMSound(short frequency,short block)
{ short fnh, fnl, blfnh, kblfnh;
fnl = frequency&0x00ff;
fnh = frequency>>8;
blfnh = fnh|(block<<2);
kblfnh = blfnh|0x20;
FMWrite(0xa0,fnl);
FMWrite(0xb0,kblfnh);
}
void FMSoundOff(short frequency,short block)
{ short fnh, blfnh, kblfnh;
fnh = frequency>>8;
blfnh = fnh|(block<<2);
kblfnh = blfnh|0x00;
FMWrite(0xb0,kblfnh);
}
void FM(short frequency,short block)
{ FMSoundOff(frequency,block);
FMSound(frequency,block);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -