📄 midi.c
字号:
TRACE("(%04X);\n", wDevID); if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID; if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE; MidiInDev[wDevID].state = 1; MidiInDev[wDevID].startTime = GetTickCount(); return MMSYSERR_NOERROR;}/************************************************************************** * midStop [internal] */static DWORD midStop(WORD wDevID){ TRACE("(%04X);\n", wDevID); if (wDevID >= MIDM_NumDevs) return MMSYSERR_BADDEVICEID; if (MidiInDev[wDevID].state == -1) return MIDIERR_NODEVICE; MidiInDev[wDevID].state = 0; return MMSYSERR_NOERROR;}/*-----------------------------------------------------------------------*/typedef struct sVoice { int note; /* 0 means not used */ int channel; unsigned cntMark : 30, status : 2;#define sVS_UNUSED 0#define sVS_PLAYING 1#define sVS_SUSTAINED 2} sVoice;typedef struct sChannel { int program; int bender; int benderRange; /* controlers */ int bank; /* CTL_BANK_SELECT */ int volume; /* CTL_MAIN_VOLUME */ int balance; /* CTL_BALANCE */ int expression; /* CTL_EXPRESSION */ int sustain; /* CTL_SUSTAIN */ unsigned char nrgPmtMSB; /* Non register Parameters */ unsigned char nrgPmtLSB; unsigned char regPmtMSB; /* Non register Parameters */ unsigned char regPmtLSB;} sChannel;typedef struct sFMextra { unsigned counter; int drumSetMask; sChannel channel[16]; /* MIDI has only 16 channels */ sVoice voice[1]; /* dyn allocated according to sound card */ /* do not append fields below voice[1] since the size of this structure * depends on the number of available voices on the FM synth... */} sFMextra;extern unsigned char midiFMInstrumentPatches[16 * 128];extern unsigned char midiFMDrumsPatches [16 * 128];/************************************************************************** * modFMLoad [internal] */static int modFMLoad(int dev){ int i; struct sbi_instrument sbi; sbi.device = dev; sbi.key = FM_PATCH; memset(sbi.operators + 16, 0, 16); for (i = 0; i < 128; i++) { sbi.channel = i; memcpy(sbi.operators, midiFMInstrumentPatches + i * 16, 16); if (write(midiSeqFD, (char*)&sbi, sizeof(sbi)) == -1) { WARN("Couldn't write patch for instrument %d, errno %d (%s)!\n", sbi.channel, errno, strerror(errno)); return -1; } } for (i = 0; i < 128; i++) { sbi.channel = 128 + i; memcpy(sbi.operators, midiFMDrumsPatches + i * 16, 16); if (write(midiSeqFD, (char*)&sbi, sizeof(sbi)) == -1) { WARN("Couldn't write patch for drum %d, errno %d (%s)!\n", sbi.channel, errno, strerror(errno)); return -1; } } return 0;}/************************************************************************** * modFMReset [internal] */static void modFMReset(WORD wDevID){ sFMextra* extra = (sFMextra*)MidiOutDev[wDevID].lpExtra; sVoice* voice = extra->voice; sChannel* channel = extra->channel; int i; for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) { if (voice[i].status != sVS_UNUSED) { SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64); } SEQ_KEY_PRESSURE(wDevID, i, 127, 0); SEQ_CONTROL(wDevID, i, SEQ_VOLMODE, VOL_METHOD_LINEAR); voice[i].note = 0; voice[i].channel = -1; voice[i].cntMark = 0; voice[i].status = sVS_UNUSED; } for (i = 0; i < 16; i++) { channel[i].program = 0; channel[i].bender = 8192; channel[i].benderRange = 2; channel[i].bank = 0; channel[i].volume = 127; channel[i].balance = 64; channel[i].expression = 0; channel[i].sustain = 0; } extra->counter = 0; extra->drumSetMask = 1 << 9; /* channel 10 is normally drums, sometimes 16 also */ SEQ_DUMPBUF();}#define IS_DRUM_CHANNEL(_xtra, _chn) ((_xtra)->drumSetMask & (1 << (_chn)))/************************************************************************** * modGetDevCaps [internal] */static DWORD modGetDevCaps(WORD wDevID, LPMIDIOUTCAPSA lpCaps, DWORD dwSize){ TRACE("(%04X, %p, %08lX);\n", wDevID, lpCaps, dwSize); if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID; if (lpCaps == NULL) return MMSYSERR_INVALPARAM; memcpy(lpCaps, &MidiOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps))); return MMSYSERR_NOERROR;}/************************************************************************** * modOpen [internal] */static DWORD modOpen(WORD wDevID, LPMIDIOPENDESC lpDesc, DWORD dwFlags){ TRACE("(%04X, %p, %08lX);\n", wDevID, lpDesc, dwFlags); if (lpDesc == NULL) { WARN("Invalid Parameter !\n"); return MMSYSERR_INVALPARAM; } if (wDevID >= MODM_NumDevs) { TRACE("MAX_MIDIOUTDRV reached !\n"); return MMSYSERR_BADDEVICEID; } if (MidiOutDev[wDevID].midiDesc.hMidi != 0) { WARN("device already open !\n"); return MMSYSERR_ALLOCATED; } if (!MidiOutDev[wDevID].bEnabled) { WARN("device disabled !\n"); return MIDIERR_NODEVICE; } if ((dwFlags & ~CALLBACK_TYPEMASK) != 0) { WARN("bad dwFlags\n"); return MMSYSERR_INVALFLAG; } if (!MidiOutDev[wDevID].bEnabled) { TRACE("disabled wDevID\n"); return MMSYSERR_NOTENABLED; } MidiOutDev[wDevID].lpExtra = 0; switch (MidiOutDev[wDevID].caps.wTechnology) { case MOD_FMSYNTH: { void* extra; extra = HeapAlloc(GetProcessHeap(), 0, sizeof(struct sFMextra) + sizeof(struct sVoice) * (MidiOutDev[wDevID].caps.wVoices - 1)); if (extra == 0) { WARN("can't alloc extra data !\n"); return MMSYSERR_NOMEM; } MidiOutDev[wDevID].lpExtra = extra; if (midiOpenSeq() < 0) { MidiOutDev[wDevID].lpExtra = 0; HeapFree(GetProcessHeap(), 0, extra); return MMSYSERR_ERROR; } if (modFMLoad(wDevID) < 0) { midiCloseSeq(); MidiOutDev[wDevID].lpExtra = 0; HeapFree(GetProcessHeap(), 0, extra); return MMSYSERR_ERROR; } modFMReset(wDevID); } break; case MOD_MIDIPORT: case MOD_SYNTH: if (midiOpenSeq() < 0) { return MMSYSERR_ALLOCATED; } break; default: WARN("Technology not supported (yet) %d !\n", MidiOutDev[wDevID].caps.wTechnology); return MMSYSERR_NOTENABLED; } MidiOutDev[wDevID].wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK); MidiOutDev[wDevID].lpQueueHdr = NULL; MidiOutDev[wDevID].dwTotalPlayed = 0; MidiOutDev[wDevID].bufsize = 0x3FFF; MidiOutDev[wDevID].midiDesc = *lpDesc; if (MIDI_NotifyClient(wDevID, MOM_OPEN, 0L, 0L) != MMSYSERR_NOERROR) { WARN("can't notify client !\n"); return MMSYSERR_INVALPARAM; } TRACE("Successful !\n"); return MMSYSERR_NOERROR;}/************************************************************************** * modClose [internal] */static DWORD modClose(WORD wDevID){ int ret = MMSYSERR_NOERROR; TRACE("(%04X);\n", wDevID); if (MidiOutDev[wDevID].midiDesc.hMidi == 0) { WARN("device not opened !\n"); return MMSYSERR_ERROR; } /* FIXME: should test that no pending buffer is still in the queue for * playing */ if (midiSeqFD == -1) { WARN("can't close !\n"); return MMSYSERR_ERROR; } switch (MidiOutDev[wDevID].caps.wTechnology) { case MOD_FMSYNTH: case MOD_MIDIPORT: midiCloseSeq(); break; default: WARN("Technology not supported (yet) %d !\n", MidiOutDev[wDevID].caps.wTechnology); return MMSYSERR_NOTENABLED; } if (MidiOutDev[wDevID].lpExtra != 0) { HeapFree(GetProcessHeap(), 0, MidiOutDev[wDevID].lpExtra); MidiOutDev[wDevID].lpExtra = 0; } MidiOutDev[wDevID].bufsize = 0; if (MIDI_NotifyClient(wDevID, MOM_CLOSE, 0L, 0L) != MMSYSERR_NOERROR) { WARN("can't notify client !\n"); ret = MMSYSERR_INVALPARAM; } MidiOutDev[wDevID].midiDesc.hMidi = 0; return ret;}/************************************************************************** * modData [internal] */static DWORD modData(WORD wDevID, DWORD dwParam){ WORD evt = LOBYTE(LOWORD(dwParam)); WORD d1 = HIBYTE(LOWORD(dwParam)); WORD d2 = LOBYTE(HIWORD(dwParam)); TRACE("(%04X, %08lX);\n", wDevID, dwParam); if (wDevID >= MODM_NumDevs) return MMSYSERR_BADDEVICEID; if (!MidiOutDev[wDevID].bEnabled) return MIDIERR_NODEVICE; if (midiSeqFD == -1) { WARN("can't play !\n"); return MIDIERR_NODEVICE; } switch (MidiOutDev[wDevID].caps.wTechnology) { case MOD_FMSYNTH: /* FIXME: * - chorus depth controller is not used */ { sFMextra* extra = (sFMextra*)MidiOutDev[wDevID].lpExtra; sVoice* voice = extra->voice; sChannel* channel = extra->channel; int chn = (evt & 0x0F); int i, nv; switch (evt & 0xF0) { case MIDI_NOTEOFF: for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) { /* don't stop sustained notes */ if (voice[i].status == sVS_PLAYING && voice[i].channel == chn && voice[i].note == d1) { voice[i].status = sVS_UNUSED; SEQ_STOP_NOTE(wDevID, i, d1, d2); } } break; case MIDI_NOTEON: if (d2 == 0) { /* note off if velocity == 0 */ for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) { /* don't stop sustained notes */ if (voice[i].status == sVS_PLAYING && voice[i].channel == chn && voice[i].note == d1) { voice[i].status = sVS_UNUSED; SEQ_STOP_NOTE(wDevID, i, d1, 64); } } break; } /* finding out in this order : * - an empty voice * - if replaying the same note on the same channel * - the older voice (LRU) */ for (i = nv = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) { if (voice[i].status == sVS_UNUSED || (voice[i].note == d1 && voice[i].channel == chn)) { nv = i; break; } if (voice[i].cntMark < voice[0].cntMark) { nv = i; } } TRACE( "playing on voice=%d, pgm=%d, pan=0x%02X, vol=0x%02X, " "bender=0x%02X, note=0x%02X, vel=0x%02X\n", nv, channel[chn].program, channel[chn].balance, channel[chn].volume, channel[chn].bender, d1, d2); SEQ_SET_PATCH(wDevID, nv, IS_DRUM_CHANNEL(extra, chn) ? (128 + d1) : channel[chn].program); SEQ_BENDER_RANGE(wDevID, nv, channel[chn].benderRange * 100); SEQ_BENDER(wDevID, nv, channel[chn].bender); SEQ_CONTROL(wDevID, nv, CTL_PAN, channel[chn].balance); SEQ_CONTROL(wDevID, nv, CTL_EXPRESSION, channel[chn].expression);#if 0 /* FIXME: does not really seem to work on my SB card and * screws everything up... so lay it down */ SEQ_CONTROL(wDevID, nv, CTL_MAIN_VOLUME, channel[chn].volume);#endif SEQ_START_NOTE(wDevID, nv, d1, d2); voice[nv].status = channel[chn].sustain ? sVS_SUSTAINED : sVS_PLAYING; voice[nv].note = d1; voice[nv].channel = chn; voice[nv].cntMark = extra->counter++; break; case MIDI_KEY_PRESSURE: for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) { if (voice[i].status != sVS_UNUSED && voice[i].channel == chn && voice[i].note == d1) { SEQ_KEY_PRESSURE(wDevID, i, d1, d2); } } break; case MIDI_CTL_CHANGE: switch (d1) { case CTL_BANK_SELECT: channel[chn].bank = d2; break; case CTL_MAIN_VOLUME: channel[chn].volume = d2; break; case CTL_PAN: channel[chn].balance = d2; break; case CTL_EXPRESSION: channel[chn].expression = d2; break; case CTL_SUSTAIN: channel[chn].sustain = d2; if (d2) { for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) { if (voice[i].status == sVS_PLAYING && voice[i].channel == chn) { voice[i].status = sVS_SUSTAINED; } } } else { for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) { if (voice[i].status == sVS_SUSTAINED && voice[i].channel == chn) { voice[i].status = sVS_UNUSED; SEQ_STOP_NOTE(wDevID, i, voice[i].note, 64); } } } break; case CTL_NONREG_PARM_NUM_LSB: channel[chn].nrgPmtLSB = d2; break; case CTL_NONREG_PARM_NUM_MSB: channel[chn].nrgPmtMSB = d2; break; case CTL_REGIST_PARM_NUM_LSB: channel[chn].regPmtLSB = d2; break; case CTL_REGIST_PARM_NUM_MSB: channel[chn].regPmtMSB = d2; break; case CTL_DATA_ENTRY: switch ((channel[chn].regPmtMSB << 8) | channel[chn].regPmtLSB) { case 0x0000: if (channel[chn].benderRange != d2) { channel[chn].benderRange = d2; for (i = 0; i < MidiOutDev[wDevID].caps.wVoices; i++) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -