From 4d84d14ac1aa13958eaa2971b03f7f929a519105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Fri, 8 Feb 2008 13:00:32 +0000 Subject: reorganized svn path=/trunk/; revision=9400 --- desiredata/src/s_midi_mmio.c | 505 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 505 insertions(+) create mode 100644 desiredata/src/s_midi_mmio.c (limited to 'desiredata/src/s_midi_mmio.c') diff --git a/desiredata/src/s_midi_mmio.c b/desiredata/src/s_midi_mmio.c new file mode 100644 index 00000000..95c95d76 --- /dev/null +++ b/desiredata/src/s_midi_mmio.c @@ -0,0 +1,505 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#include "m_pd.h" +#include "s_stuff.h" +#include +#include +#include + +/* ------------- MIDI time stamping from audio clock ------------ */ +#ifdef MIDI_TIMESTAMP + +static double msw_hibuftime; +static double initsystime = -1; + +/* call this whenever we reset audio */ +static void msw_resetmidisync() { + initsystime = clock_getsystime(); + msw_hibuftime = sys_getrealtime(); +} + +/* call this whenever we're idled waiting for audio to be ready. The routine maintains a high and low water point + for the difference between real and DAC time. */ +static void msw_midisync() { + if (initsystime == -1) msw_resetmidisync(); + double jittersec = max(msw_dacjitterbufsallowed,msw_adcjitterbufsallowed) * REALDACBLKSIZE / sys_getsr(); + double diff = sys_getrealtime() - 0.001 * clock_gettimesince(initsystime); + if (diff > msw_hibuftime) msw_hibuftime = diff; + if (diff < msw_hibuftime - jittersec) { + post("jitter excess %d %f", dac, diff); + msw_resetmidisync(); + } +} + +static double msw_midigettimefor(LARGE_INTEGER timestamp) { + /* this is broken now... used to work when "timestamp" was derived from QueryPerformanceCounter() instead of + the MS-approved timeGetSystemTime() call in the MIDI callback routine below. */ + return msw_tixtotime(timestamp) - msw_hibuftime; +} +#endif /* MIDI_TIMESTAMP */ + +/* ------------------------- MIDI output -------------------------- */ +static void msw_midiouterror(char *s, int err) { + char t[256]; + midiOutGetErrorText(err, t, 256); + error(s,t); +} + +static HMIDIOUT hMidiOut[MAXMIDIOUTDEV]; /* output device */ +static int msw_nmidiout; /* number of devices */ + +static void msw_open_midiout(int nmidiout, int *midioutvec) { + UINT result, wRtn; + MIDIOUTCAPS midioutcaps; + if (nmidiout > MAXMIDIOUTDEV) nmidiout = MAXMIDIOUTDEV; + int dev = 0; + for (int i=0; ihSelf = hMem; + return lpBuf; +} + +/* FreeCallbackInstanceData - Frees the given CALLBACKINSTANCEDATA structure. + * Params: lpBuf - Points to the CALLBACKINSTANCEDATA structure to be freed. */ +void FAR PASCAL FreeCallbackInstanceData(LPCALLBACKINSTANCEDATA lpBuf) { + HANDLE hMem; + /* Save the handle until we're through here. */ + hMem = lpBuf->hSelf; + /* Free the structure. */ + //GlobalPageUnlock((HGLOBAL)HIWORD(lpBuf)); + GlobalUnlock(hMem); + GlobalFree(hMem); +} + +/* AllocCircularBuffer - Allocates memory for a CIRCULARBUFFER structure + * and a buffer of the specified size. Each memory block is allocated + * with GlobalAlloc() using GMEM_SHARE and GMEM_MOVEABLE flags, locked + * with GlobalLock(), and page-locked with GlobalPageLock(). + * Params: dwSize - The size of the buffer, in events. + * Return: A pointer to a CIRCULARBUFFER structure identifying the allocated display buffer. NULL if the buffer could not be allocated. */ + +LPCIRCULARBUFFER AllocCircularBuffer(DWORD dwSize) { + HANDLE hMem; + LPCIRCULARBUFFER lpBuf; + LPEVENT lpMem; + /* Allocate and lock a CIRCULARBUFFER structure. */ + hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, (DWORD)sizeof(CIRCULARBUFFER)); + if(!hMem) return 0; + lpBuf = (LPCIRCULARBUFFER)GlobalLock(hMem); + if(!lpBuf) {GlobalFree(hMem); return 0;} + /* Page lock the memory. Global memory blocks accessed by low-level callback functions must be page locked. */ +#ifndef _WIN32 + GlobalSmartPageLock((HGLOBAL)HIWORD(lpBuf)); +#endif + /* Save the memory handle. */ + lpBuf->hSelf = hMem; + /* Allocate and lock memory for the actual buffer. */ + hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, dwSize * sizeof(EVENT)); + if(!hMem) { +#ifndef _WIN32 + GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf)); +#endif + GlobalUnlock(lpBuf->hSelf); + GlobalFree(lpBuf->hSelf); + return 0; + } + lpMem = (LPEVENT)GlobalLock(hMem); + if(!lpMem) { + GlobalFree(hMem); +#ifndef _WIN32 + GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf)); +#endif + GlobalUnlock(lpBuf->hSelf); + GlobalFree(lpBuf->hSelf); + return NULL; + } + /* Page lock the memory. Global memory blocks accessed by low-level callback functions must be page locked. */ +#ifndef _WIN32 + GlobalSmartPageLock((HGLOBAL)HIWORD(lpMem)); +#endif + /* Set up the CIRCULARBUFFER structure. */ + lpBuf->hBuffer = hMem; + lpBuf->wError = 0; + lpBuf->dwSize = dwSize; + lpBuf->dwCount = 0L; + lpBuf->lpStart = lpMem; + lpBuf->lpEnd = lpMem + dwSize; + lpBuf->lpTail = lpMem; + lpBuf->lpHead = lpMem; + return lpBuf; +} + +/* FreeCircularBuffer - Frees the memory for the given CIRCULARBUFFER structure and the memory for the buffer it references. + * Params: lpBuf - Points to the CIRCULARBUFFER to be freed. */ +void FreeCircularBuffer(LPCIRCULARBUFFER lpBuf) { + HANDLE hMem; + /* Free the buffer itself. */ +#ifndef _WIN32 + GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf->lpStart)); +#endif + GlobalUnlock(lpBuf->hBuffer); + GlobalFree(lpBuf->hBuffer); + /* Free the CIRCULARBUFFER structure. */ + hMem = lpBuf->hSelf; +#ifndef _WIN32 + GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf)); +#endif + GlobalUnlock(hMem); + GlobalFree(hMem); +} + +/* GetEvent - Gets a MIDI event from the circular input buffer. Events + * are removed from the buffer. The corresponding PutEvent() function + * is called by the low-level callback function, so it must reside in + * the callback DLL. PutEvent() is defined in the CALLBACK.C module. + * + * Params: lpBuf - Points to the circular buffer. + * lpEvent - Points to an EVENT structure that is filled with the retrieved event. + * Return: Returns non-zero if successful, zero if there are no events to get. */ +WORD FAR PASCAL GetEvent(LPCIRCULARBUFFER lpBuf, LPEVENT lpEvent) { + /* If no event available, return */ + if (!wNumDevices || lpBuf->dwCount <= 0) return 0; + /* Get the event. */ + *lpEvent = *lpBuf->lpTail; + /* Decrement the byte count, bump the tail pointer. */ + --lpBuf->dwCount; + ++lpBuf->lpTail; + /* Wrap the tail pointer, if necessary. */ + if (lpBuf->lpTail >= lpBuf->lpEnd) lpBuf->lpTail = lpBuf->lpStart; + return 1; +} + +/* PutEvent - Puts an EVENT in a CIRCULARBUFFER. If the buffer is full, + * it sets the wError element of the CIRCULARBUFFER structure + * to be non-zero. + * + * Params: lpBuf - Points to the CIRCULARBUFFER. + * lpEvent - Points to the EVENT. */ + +void FAR PASCAL PutEvent(LPCIRCULARBUFFER lpBuf, LPEVENT lpEvent) { + /* If the buffer is full, set an error and return. */ + if(lpBuf->dwCount >= lpBuf->dwSize){ + lpBuf->wError = 1; + return; + } + /* Put the event in the buffer, bump the head pointer and the byte count. */ + *lpBuf->lpHead = *lpEvent; + ++lpBuf->lpHead; + ++lpBuf->dwCount; + /* Wrap the head pointer, if necessary. */ + if(lpBuf->lpHead >= lpBuf->lpEnd) lpBuf->lpHead = lpBuf->lpStart; +} + +/* midiInputHandler - Low-level callback function to handle MIDI input. + * Installed by midiInOpen(). The input handler takes incoming + * MIDI events and places them in the circular input buffer. It then + * notifies the application by posting a MM_MIDIINPUT message. + * + * This function is accessed at interrupt time, so it should be as + * fast and efficient as possible. You can't make any + * Windows calls here, except PostMessage(). The only Multimedia + * Windows call you can make are timeGetSystemTime(), midiOutShortMsg(). + * + * Param: hMidiIn - Handle for the associated input device. + * wMsg - One of the MIM_***** messages. + * dwInstance - Points to CALLBACKINSTANCEDATA structure. + * dwParam1 - MIDI data. + * dwParam2 - Timestamp (in milliseconds) */ +void FAR PASCAL midiInputHandler(HMIDIIN hMidiIn, WORD wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) { + EVENT event; + switch(wMsg) { + case MIM_OPEN: break; + /* The only error possible is invalid MIDI data, so just pass the invalid data on so we'll see it. */ + case MIM_ERROR: + case MIM_DATA: + event.fdwEvent = (wMsg == MIM_ERROR) ? EVNT_F_ERROR : 0; + event.dwDevice = ((LPCALLBACKINSTANCEDATA)dwInstance)->dwDevice; + event.data = dwParam1; +#ifdef MIDI_TIMESTAMP + event.timestamp = timeGetSystemTime(); +#endif + /* Put the MIDI event in the circular input buffer. */ + PutEvent(((LPCALLBACKINSTANCEDATA)dwInstance)->lpBuf, (LPEVENT)&event); + break; + default: break; + } +} + +void msw_open_midiin(int nmidiin, int *midiinvec) { + UINT wRtn; + char szErrorText[256]; + unsigned int i; + unsigned int ndev = 0; + /* Allocate a circular buffer for low-level MIDI input. This buffer is filled by the low-level callback function + and emptied by the application. */ + lpInputBuffer = AllocCircularBuffer((DWORD)(INPUT_BUFFER_SIZE)); + if (!lpInputBuffer) { + printf("Not enough memory available for input buffer.\n"); + return; + } + /* Open all MIDI input devices after allocating and setting up instance data for each device. The instance data is + used to pass buffer management information between the application and the low-level callback function. It also + includes a device ID, a handle to the MIDI Mapper, and a handle to the application's display window, so the callback + can notify the window when input data is available. A single callback function is used to service all opened input devices. */ + for (i=0; (i<(unsigned)nmidiin) && (idwDevice = i; + lpCallbackInstanceData[i]->lpBuf = lpInputBuffer; + wRtn = midiInOpen((LPHMIDIIN)&hMidiIn[ndev], midiinvec[i], (DWORD)midiInputHandler, + (DWORD)lpCallbackInstanceData[ndev], CALLBACK_FUNCTION); + if (wRtn) { + FreeCallbackInstanceData(lpCallbackInstanceData[ndev]); + msw_midiinerror("midiInOpen: %s", wRtn); + } else ndev++; + } + /* Start MIDI input. */ + for (i=0; i= 0 && portno < msw_nmidiout) { + foo = (a & 0xff) | ((b & 0xff) << 8) | ((c & 0xff) << 16); + res = midiOutShortMsg(hMidiOut[portno], foo); + if (res != MMSYSERR_NOERROR) post("MIDI out error %d", res); + } +} + +void sys_putmidibyte(int portno, int byte) { + MMRESULT res; + if (portno >= 0 && portno < msw_nmidiout) { + res = midiOutShortMsg(hMidiOut[portno], byte); + if (res != MMSYSERR_NOERROR) post("MIDI out error %d", res); + } +} + +void sys_poll_midi() { + static EVENT msw_nextevent; + static int msw_isnextevent; + static double msw_nexteventtime; + while (1) { + if (!msw_isnextevent) { + if (!GetEvent(lpInputBuffer, &msw_nextevent)) break; + msw_isnextevent = 1; +#ifdef MIDI_TIMESTAMP + msw_nexteventtime = msw_midigettimefor(&foo.timestamp); +#endif + } +#ifdef MIDI_TIMESTAMP + if (0.001 * clock_gettimesince(initsystime) >= msw_nexteventtime) +#endif + { + int msgtype = ((msw_nextevent.data & 0xf0) >> 4) - 8; + int commandbyte = msw_nextevent.data & 0xff; + int byte1 = (msw_nextevent.data >> 8) & 0xff; + int byte2 = (msw_nextevent.data >> 16) & 0xff; + int portno = msw_nextevent.dwDevice; + switch (msgtype) { + case 0: + case 1: + case 2: + case 3: + case 6: + sys_midibytein(portno, commandbyte); + sys_midibytein(portno, byte1); + sys_midibytein(portno, byte2); + break; + case 4: + case 5: + sys_midibytein(portno, commandbyte); + sys_midibytein(portno, byte1); + break; + case 7: + sys_midibytein(portno, commandbyte); + break; + } + msw_isnextevent = 0; + } + } +} + +void sys_do_open_midi(int nmidiin, int *midiinvec, int nmidiout, int *midioutvec) { + if (nmidiout) msw_open_midiout(nmidiout, midioutvec); + if (nmidiin) { + post("Warning: midi input is dangerous in Microsoft Windows; see Pd manual)"); + msw_open_midiin(nmidiin, midiinvec); + } +} + +void sys_close_midi() { + msw_close_midiin(); + msw_close_midiout(); +} + +#if 0 +/* list the audio and MIDI device names */ +void sys_listmididevs() { + UINT wRtn, ndevices; + unsigned int i; + /* for MIDI and audio in and out, get the number of devices. Then get the capabilities of each device and print its description. */ + ndevices = midiInGetNumDevs(); + for (i = 0; i < ndevices; i++) { + MIDIINCAPS micap; + wRtn = midiInGetDevCaps(i, (LPMIDIINCAPS) &micap, sizeof(micap)); + if (wRtn) msw_midiinerror("midiInGetDevCaps: %s", wRtn); + else error("MIDI input device #%d: %s", i+1, micap.szPname); + } + ndevices = midiOutGetNumDevs(); + for (i = 0; i < ndevices; i++) { + MIDIOUTCAPS mocap; + wRtn = midiOutGetDevCaps(i, (LPMIDIOUTCAPS) &mocap, sizeof(mocap)); + if (wRtn) msw_midiouterror("midiOutGetDevCaps: %s", wRtn); + else error("MIDI output device #%d: %s", i+1, mocap.szPname); + } +} +#endif + +void midi_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int maxndev, int devdescsize) { + int i, nin = midiInGetNumDevs(), nout = midiOutGetNumDevs(); + UINT wRtn; + if (nin > maxndev) nin = maxndev; + for (i = 0; i < nin; i++) { + MIDIINCAPS micap; + wRtn = midiInGetDevCaps(i, (LPMIDIINCAPS) &micap, sizeof(micap)); + strncpy(indevlist + i * devdescsize, (wRtn ? "???" : micap.szPname), devdescsize); + indevlist[(i+1) * devdescsize - 1] = 0; + } + if (nout > maxndev) nout = maxndev; + for (i = 0; i < nout; i++) { + MIDIOUTCAPS mocap; + wRtn = midiOutGetDevCaps(i, (LPMIDIOUTCAPS) &mocap, sizeof(mocap)); + strncpy(outdevlist + i * devdescsize, (wRtn ? "???" : mocap.szPname), devdescsize); + outdevlist[(i+1) * devdescsize - 1] = 0; + } + *nindevs = nin; + *noutdevs = nout; +} -- cgit v1.2.1