#include "midi.h" #include #include #include #include extern atomic_int gRunning; static void CALLBACK midiCallbackWin32(HMIDIIN hIn, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2) { (void)hIn; (void)param2; if (msg != MIM_DATA) return; MidiState *m = (MidiState *)instance; BYTE status = (BYTE)( param1 & 0xFF); BYTE data1 = (BYTE)((param1 >> 8) & 0xFF); BYTE data2 = (BYTE)((param1 >> 16) & 0xFF); BYTE type = status & 0xF0; if (type == 0x90 && data2 > 0) { synthNoteOn(m->synth, data1); } else if (type == 0x80 || (type == 0x90 && data2 == 0)) { synthNoteOff(m->synth, data1); } else if (type == 0xB0) { int cc = data1, val = data2; if (cc < 128) { if (atomic_load(&m->midiLearnMode)) atomic_store(&m->midiLearnCC, cc); if (m->ccMappings[cc].active) { float t = (float)val / 127.0f; *m->ccMappings[cc].valuePtr = m->ccMappings[cc].min + t * (m->ccMappings[cc].max - m->ccMappings[cc].min); } } } else if (type == 0xE0) { int pb = (data2 << 7) | data1; // 0..16383, centre = 8192 m->synth->pitchBend = (pb - 8192) / 8191.0f; } } int midiInit(MidiState *m, Synth *synth) { m->synth = synth; m->hMidiIn = NULL; m->connectedClient = -1; m->connectedPort = -1; m->inputCount = 0; atomic_store(&m->midiLearnMode, 0); atomic_store(&m->midiLearnCC, -1); for (int i = 0; i < 128; i++) m->ccMappings[i].active = 0; return 1; } void midiScanInputs(MidiState *m) { m->inputCount = 0; UINT n = midiInGetNumDevs(); for (UINT i = 0; i < n && m->inputCount < MIDI_MAX_INPUTS; i++) { MIDIINCAPSA caps; if (midiInGetDevCapsA(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR) { MidiInputInfo *info = &m->inputs[m->inputCount++]; info->client = (int)i; info->port = 0; snprintf(info->clientName, sizeof(info->clientName), "%s", caps.szPname); snprintf(info->portName, sizeof(info->portName), "%s", caps.szPname); } } } void midiDevDisconnect(MidiState *m) { if (m->hMidiIn) { midiInStop((HMIDIIN)m->hMidiIn); midiInClose((HMIDIIN)m->hMidiIn); m->hMidiIn = NULL; } m->connectedClient = -1; m->connectedPort = -1; } void midiDevConnect(MidiState *m, int srcClient, int srcPort) { (void)srcPort; midiDevDisconnect(m); HMIDIIN hIn = NULL; if (midiInOpen(&hIn, (UINT)srcClient, (DWORD_PTR)midiCallbackWin32, (DWORD_PTR)m, CALLBACK_FUNCTION) == MMSYSERR_NOERROR) { midiInStart(hIn); m->hMidiIn = hIn; m->connectedClient = srcClient; m->connectedPort = 0; } } void midiClose(MidiState *m) { midiDevDisconnect(m); } void *midiThread(void *arg) { // WinMM delivers MIDI events via midiCallbackWin32 on its own MM thread. // This function exists only to satisfy the pthread_create call in soundThing.c. (void)arg; while (gRunning) Sleep(50); return NULL; }