1
/* Copyright (c) 1997-1999 Miller Puckette.
2
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
3
* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
12
/* ------------- MIDI time stamping from audio clock ------------ */
16
static double msw_hibuftime;
17
static double initsystime = -1;
19
/* call this whenever we reset audio */
20
static void msw_resetmidisync(void)
22
initsystime = clock_getsystime();
23
msw_hibuftime = sys_getrealtime();
26
/* call this whenever we're idled waiting for audio to be ready.
27
The routine maintains a high and low water point for the difference
28
between real and DAC time. */
30
static void msw_midisync(void)
32
double jittersec, diff;
34
if (initsystime == -1) msw_resetmidisync();
35
jittersec = (msw_dacjitterbufsallowed > msw_adcjitterbufsallowed ?
36
msw_dacjitterbufsallowed : msw_adcjitterbufsallowed)
37
* REALDACBLKSIZE / sys_getsr();
38
diff = sys_getrealtime() - 0.001 * clock_gettimesince(initsystime);
39
if (diff > msw_hibuftime) msw_hibuftime = diff;
40
if (diff < msw_hibuftime - jittersec)
42
post("jitter excess %d %f", dac, diff);
47
static double msw_midigettimefor(LARGE_INTEGER timestamp)
49
/* this is broken now... used to work when "timestamp" was derived from
50
QueryPerformanceCounter() instead of the gates approved
51
timeGetSystemTime() call in the MIDI callback routine below. */
52
return (msw_tixtotime(timestamp) - msw_hibuftime);
54
#endif /* MIDI_TIMESTAMP */
57
/* ------------------------- MIDI output -------------------------- */
58
static void msw_midiouterror(char *s, int err)
61
midiOutGetErrorText(err, t, 256);
62
fprintf(stderr, s, t);
65
static HMIDIOUT hMidiOut[MAXMIDIOUTDEV]; /* output device */
66
static int msw_nmidiout; /* number of devices */
68
static void msw_open_midiout(int nmidiout, int *midioutvec)
73
MIDIOUTCAPS midioutcaps;
74
if (nmidiout > MAXMIDIOUTDEV)
75
nmidiout = MAXMIDIOUTDEV;
79
for (i = 0; i < nmidiout; i++)
82
int devno = midioutvec[i];
83
result = midiOutOpen(&hMidiOut[dev], devno, 0, 0,
85
wRtn = midiOutGetDevCaps(i, (LPMIDIOUTCAPS) &mocap,
87
if (result != MMSYSERR_NOERROR)
89
fprintf(stderr,"midiOutOpen: %s\n",midioutcaps.szPname);
90
msw_midiouterror("midiOutOpen: %s\n", result);
95
fprintf(stderr,"midiOutOpen: Open %s as Port %d\n",
96
midioutcaps.szPname, dev);
103
static void msw_close_midiout(void)
106
for (i = 0; i < msw_nmidiout; i++)
108
midiOutReset(hMidiOut[i]);
109
midiOutClose(hMidiOut[i]);
114
/* -------------------------- MIDI input ---------------------------- */
116
#define INPUT_BUFFER_SIZE 1000 // size of input buffer in events
118
static void msw_midiinerror(char *s, int err)
121
midiInGetErrorText(err, t, 256);
122
fprintf(stderr, s, t);
126
/* Structure to represent a single MIDI event.
129
#define EVNT_F_ERROR 0x00000001L
131
typedef struct evemsw_tag
135
LARGE_INTEGER timestamp;
138
typedef EVENT FAR *LPEVENT;
140
/* Structure to manage the circular input buffer.
142
typedef struct circularBuffer_tag
144
HANDLE hSelf; /* handle to this structure */
145
HANDLE hBuffer; /* buffer handle */
146
WORD wError; /* error flags */
147
DWORD dwSize; /* buffer size (in EVENTS) */
148
DWORD dwCount; /* byte count (in EVENTS) */
149
LPEVENT lpStart; /* ptr to start of buffer */
150
LPEVENT lpEnd; /* ptr to end of buffer (last byte + 1) */
151
LPEVENT lpHead; /* ptr to head (next location to fill) */
152
LPEVENT lpTail; /* ptr to tail (next location to empty) */
154
typedef CIRCULARBUFFER FAR *LPCIRCULARBUFFER;
157
/* Structure to pass instance data from the application
158
to the low-level callback function.
160
typedef struct callbackInstance_tag
164
LPCIRCULARBUFFER lpBuf;
165
} CALLBACKINSTANCEDATA;
166
typedef CALLBACKINSTANCEDATA FAR *LPCALLBACKINSTANCEDATA;
168
/* Function prototypes
170
LPCALLBACKINSTANCEDATA FAR PASCAL AllocCallbackInstanceData(void);
171
void FAR PASCAL FreeCallbackInstanceData(LPCALLBACKINSTANCEDATA lpBuf);
173
LPCIRCULARBUFFER AllocCircularBuffer(DWORD dwSize);
174
void FreeCircularBuffer(LPCIRCULARBUFFER lpBuf);
175
WORD FAR PASCAL GetEvent(LPCIRCULARBUFFER lpBuf, LPEVENT lpEvent);
177
// Callback instance data pointers
178
LPCALLBACKINSTANCEDATA lpCallbackInstanceData[MAXMIDIINDEV];
180
UINT wNumDevices = 0; // Number of MIDI input devices opened
181
BOOL bRecordingEnabled = 1; // Enable/disable recording flag
182
int nNumBufferLines = 0; // Number of lines in display buffer
183
RECT rectScrollClip; // Clipping rectangle for scrolling
185
LPCIRCULARBUFFER lpInputBuffer; // Input buffer structure
186
EVENT incomingEvent; // Incoming MIDI event structure
188
MIDIINCAPS midiInCaps[MAXMIDIINDEV]; // Device capabilities structures
189
HMIDIIN hMidiIn[MAXMIDIINDEV]; // MIDI input device handles
192
/* AllocCallbackInstanceData - Allocates a CALLBACKINSTANCEDATA
193
* structure. This structure is used to pass information to the
194
* low-level callback function, each time it receives a message.
196
* Because this structure is accessed by the low-level callback
197
* function, it must be allocated using GlobalAlloc() with the
198
* GMEM_SHARE and GMEM_MOVEABLE flags and page-locked with
203
* Return: A pointer to the allocated CALLBACKINSTANCE data structure.
205
LPCALLBACKINSTANCEDATA FAR PASCAL AllocCallbackInstanceData(void)
208
LPCALLBACKINSTANCEDATA lpBuf;
210
/* Allocate and lock global memory.
212
hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE,
213
(DWORD)sizeof(CALLBACKINSTANCEDATA));
217
lpBuf = (LPCALLBACKINSTANCEDATA)GlobalLock(hMem);
223
/* Page lock the memory.
225
//GlobalPageLock((HGLOBAL)HIWORD(lpBuf));
234
/* FreeCallbackInstanceData - Frees the given CALLBACKINSTANCEDATA structure.
236
* Params: lpBuf - Points to the CALLBACKINSTANCEDATA structure to be freed.
240
void FAR PASCAL FreeCallbackInstanceData(LPCALLBACKINSTANCEDATA lpBuf)
244
/* Save the handle until we're through here.
248
/* Free the structure.
250
//GlobalPageUnlock((HGLOBAL)HIWORD(lpBuf));
257
* AllocCircularBuffer - Allocates memory for a CIRCULARBUFFER structure
258
* and a buffer of the specified size. Each memory block is allocated
259
* with GlobalAlloc() using GMEM_SHARE and GMEM_MOVEABLE flags, locked
260
* with GlobalLock(), and page-locked with GlobalPageLock().
262
* Params: dwSize - The size of the buffer, in events.
264
* Return: A pointer to a CIRCULARBUFFER structure identifying the
265
* allocated display buffer. NULL if the buffer could not be allocated.
269
LPCIRCULARBUFFER AllocCircularBuffer(DWORD dwSize)
272
LPCIRCULARBUFFER lpBuf;
275
/* Allocate and lock a CIRCULARBUFFER structure.
277
hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE,
278
(DWORD)sizeof(CIRCULARBUFFER));
282
lpBuf = (LPCIRCULARBUFFER)GlobalLock(hMem);
289
/* Page lock the memory. Global memory blocks accessed by
290
* low-level callback functions must be page locked.
293
GlobalSmartPageLock((HGLOBAL)HIWORD(lpBuf));
296
/* Save the memory handle.
300
/* Allocate and lock memory for the actual buffer.
302
hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, dwSize * sizeof(EVENT));
306
GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf));
308
GlobalUnlock(lpBuf->hSelf);
309
GlobalFree(lpBuf->hSelf);
313
lpMem = (LPEVENT)GlobalLock(hMem);
318
GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf));
320
GlobalUnlock(lpBuf->hSelf);
321
GlobalFree(lpBuf->hSelf);
325
/* Page lock the memory. Global memory blocks accessed by
326
* low-level callback functions must be page locked.
329
GlobalSmartPageLock((HGLOBAL)HIWORD(lpMem));
332
/* Set up the CIRCULARBUFFER structure.
334
lpBuf->hBuffer = hMem;
336
lpBuf->dwSize = dwSize;
338
lpBuf->lpStart = lpMem;
339
lpBuf->lpEnd = lpMem + dwSize;
340
lpBuf->lpTail = lpMem;
341
lpBuf->lpHead = lpMem;
346
/* FreeCircularBuffer - Frees the memory for the given CIRCULARBUFFER
347
* structure and the memory for the buffer it references.
349
* Params: lpBuf - Points to the CIRCULARBUFFER to be freed.
353
void FreeCircularBuffer(LPCIRCULARBUFFER lpBuf)
357
/* Free the buffer itself.
360
GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf->lpStart));
362
GlobalUnlock(lpBuf->hBuffer);
363
GlobalFree(lpBuf->hBuffer);
365
/* Free the CIRCULARBUFFER structure.
369
GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf));
375
/* GetEvent - Gets a MIDI event from the circular input buffer. Events
376
* are removed from the buffer. The corresponding PutEvent() function
377
* is called by the low-level callback function, so it must reside in
378
* the callback DLL. PutEvent() is defined in the CALLBACK.C module.
380
* Params: lpBuf - Points to the circular buffer.
381
* lpEvent - Points to an EVENT structure that is filled with the
384
* Return: Returns non-zero if successful, zero if there are no
387
WORD FAR PASCAL GetEvent(LPCIRCULARBUFFER lpBuf, LPEVENT lpEvent)
389
/* If no event available, return */
390
if (!wNumDevices || lpBuf->dwCount <= 0) return (0);
394
*lpEvent = *lpBuf->lpTail;
396
/* Decrement the byte count, bump the tail pointer.
401
/* Wrap the tail pointer, if necessary.
403
if(lpBuf->lpTail >= lpBuf->lpEnd)
404
lpBuf->lpTail = lpBuf->lpStart;
409
/* PutEvent - Puts an EVENT in a CIRCULARBUFFER. If the buffer is full,
410
* it sets the wError element of the CIRCULARBUFFER structure
413
* Params: lpBuf - Points to the CIRCULARBUFFER.
414
* lpEvent - Points to the EVENT.
419
void FAR PASCAL PutEvent(LPCIRCULARBUFFER lpBuf, LPEVENT lpEvent)
421
/* If the buffer is full, set an error and return.
423
if(lpBuf->dwCount >= lpBuf->dwSize){
428
/* Put the event in the buffer, bump the head pointer and the byte count.
430
*lpBuf->lpHead = *lpEvent;
435
/* Wrap the head pointer, if necessary.
437
if(lpBuf->lpHead >= lpBuf->lpEnd)
438
lpBuf->lpHead = lpBuf->lpStart;
441
/* midiInputHandler - Low-level callback function to handle MIDI input.
442
* Installed by midiInOpen(). The input handler takes incoming
443
* MIDI events and places them in the circular input buffer. It then
444
* notifies the application by posting a MM_MIDIINPUT message.
446
* This function is accessed at interrupt time, so it should be as
447
* fast and efficient as possible. You can't make any
448
* Windows calls here, except PostMessage(). The only Multimedia
449
* Windows call you can make are timeGetSystemTime(), midiOutShortMsg().
452
* Param: hMidiIn - Handle for the associated input device.
453
* wMsg - One of the MIM_***** messages.
454
* dwInstance - Points to CALLBACKINSTANCEDATA structure.
455
* dwParam1 - MIDI data.
456
* dwParam2 - Timestamp (in milliseconds)
460
void FAR PASCAL midiInputHandler(
474
/* The only error possible is invalid MIDI data, so just pass
475
* the invalid data on so we'll see it.
479
event.fdwEvent = (wMsg == MIM_ERROR) ? EVNT_F_ERROR : 0;
480
event.dwDevice = ((LPCALLBACKINSTANCEDATA)dwInstance)->dwDevice;
481
event.data = dwParam1;
482
#ifdef MIDI_TIMESTAMP
483
event.timestamp = timeGetSystemTime();
485
/* Put the MIDI event in the circular input buffer.
488
PutEvent(((LPCALLBACKINSTANCEDATA)dwInstance)->lpBuf,
498
void msw_open_midiin(int nmidiin, int *midiinvec)
501
char szErrorText[256];
503
unsigned int ndev = 0;
504
/* Allocate a circular buffer for low-level MIDI input. This buffer
505
* is filled by the low-level callback function and emptied by the
508
lpInputBuffer = AllocCircularBuffer((DWORD)(INPUT_BUFFER_SIZE));
509
if (lpInputBuffer == NULL)
511
printf("Not enough memory available for input buffer.\n");
515
/* Open all MIDI input devices after allocating and setting up
516
* instance data for each device. The instance data is used to
517
* pass buffer management information between the application and
518
* the low-level callback function. It also includes a device ID,
519
* a handle to the MIDI Mapper, and a handle to the application's
520
* display window, so the callback can notify the window when input
521
* data is available. A single callback function is used to service
522
* all opened input devices.
524
for (i=0; (i<(unsigned)nmidiin) && (i<MAXMIDIINDEV); i++)
526
if ((lpCallbackInstanceData[ndev] = AllocCallbackInstanceData()) == NULL)
528
printf("Not enough memory available.\n");
529
FreeCircularBuffer(lpInputBuffer);
532
lpCallbackInstanceData[i]->dwDevice = i;
533
lpCallbackInstanceData[i]->lpBuf = lpInputBuffer;
535
wRtn = midiInOpen((LPHMIDIIN)&hMidiIn[ndev],
537
(DWORD)midiInputHandler,
538
(DWORD)lpCallbackInstanceData[ndev],
542
FreeCallbackInstanceData(lpCallbackInstanceData[ndev]);
543
msw_midiinerror("midiInOpen: %s\n", wRtn);
550
for (i=0; i<ndev; i++)
553
midiInStart(hMidiIn[i]);
558
static void msw_close_midiin(void)
561
/* Stop, reset, close MIDI input. Free callback instance data.
564
for (i=0; (i<wNumDevices) && (i<MAXMIDIINDEV); i++)
569
post("closing MIDI input %d...", i);
570
midiInStop(hMidiIn[i]);
571
midiInReset(hMidiIn[i]);
572
midiInClose(hMidiIn[i]);
573
FreeCallbackInstanceData(lpCallbackInstanceData[i]);
577
/* Free input buffer.
580
FreeCircularBuffer(lpInputBuffer);
587
/* ------------------- public routines -------------------------- */
589
void sys_putmidimess(int portno, int a, int b, int c)
593
if (portno >= 0 && portno < msw_nmidiout)
595
foo = (a & 0xff) | ((b & 0xff) << 8) | ((c & 0xff) << 16);
596
res = midiOutShortMsg(hMidiOut[portno], foo);
597
if (res != MMSYSERR_NOERROR)
598
post("MIDI out error %d", res);
602
void sys_putmidibyte(int portno, int byte)
605
if (portno >= 0 && portno < msw_nmidiout)
607
res = midiOutShortMsg(hMidiOut[portno], byte);
608
if (res != MMSYSERR_NOERROR)
609
post("MIDI out error %d", res);
613
void sys_poll_midi(void)
615
static EVENT msw_nextevent;
616
static int msw_isnextevent;
617
static double msw_nexteventtime;
621
if (!msw_isnextevent)
623
if (!GetEvent(lpInputBuffer, &msw_nextevent)) break;
625
#ifdef MIDI_TIMESTAMP
626
msw_nexteventtime = msw_midigettimefor(&foo.timestamp);
629
#ifdef MIDI_TIMESTAMP
630
if (0.001 * clock_gettimesince(initsystime) >= msw_nexteventtime)
633
int msgtype = ((msw_nextevent.data & 0xf0) >> 4) - 8;
634
int commandbyte = msw_nextevent.data & 0xff;
635
int byte1 = (msw_nextevent.data >> 8) & 0xff;
636
int byte2 = (msw_nextevent.data >> 16) & 0xff;
637
int portno = msw_nextevent.dwDevice;
645
sys_midibytein(portno, commandbyte);
646
sys_midibytein(portno, byte1);
647
sys_midibytein(portno, byte2);
651
sys_midibytein(portno, commandbyte);
652
sys_midibytein(portno, byte1);
655
sys_midibytein(portno, commandbyte);
663
void sys_do_open_midi(int nmidiin, int *midiinvec,
664
int nmidiout, int *midioutvec)
667
msw_open_midiout(nmidiout, midioutvec);
671
"Warning: midi input is dangerous in Microsoft Windows; see Pd manual)");
672
msw_open_midiin(nmidiin, midiinvec);
676
void sys_close_midi( void)
683
/* list the audio and MIDI device names */
684
void sys_listmididevs(void)
689
/* for MIDI and audio in and out, get the number of devices.
690
Then get the capabilities of each device and print its description. */
692
ndevices = midiInGetNumDevs();
693
for (i = 0; i < ndevices; i++)
696
wRtn = midiInGetDevCaps(i, (LPMIDIINCAPS) &micap,
698
if (wRtn) msw_midiinerror("midiInGetDevCaps: %s\n", wRtn);
700
"MIDI input device #%d: %s\n", i+1, micap.szPname);
703
ndevices = midiOutGetNumDevs();
704
for (i = 0; i < ndevices; i++)
707
wRtn = midiOutGetDevCaps(i, (LPMIDIOUTCAPS) &mocap,
709
if (wRtn) msw_midiouterror("midiOutGetDevCaps: %s\n", wRtn);
711
"MIDI output device #%d: %s\n", i+1, mocap.szPname);
717
void midi_getdevs(char *indevlist, int *nindevs,
718
char *outdevlist, int *noutdevs, int maxndev, int devdescsize)
720
int i, nin = midiInGetNumDevs(), nout = midiOutGetNumDevs();
724
for (i = 0; i < nin; i++)
727
wRtn = midiInGetDevCaps(i, (LPMIDIINCAPS) &micap, sizeof(micap));
728
strncpy(indevlist + i * devdescsize,
729
(wRtn ? "???" : micap.szPname), devdescsize);
730
indevlist[(i+1) * devdescsize - 1] = 0;
734
for (i = 0; i < nout; i++)
737
wRtn = midiOutGetDevCaps(i, (LPMIDIOUTCAPS) &mocap, sizeof(mocap));
738
strncpy(outdevlist + i * devdescsize,
739
(wRtn ? "???" : mocap.szPname), devdescsize);
740
outdevlist[(i+1) * devdescsize - 1] = 0;