~ubuntu-branches/ubuntu/precise/puredata/precise

« back to all changes in this revision

Viewing changes to src/s_midi_mmio.c

  • Committer: Bazaar Package Importer
  • Author(s): Paul Brossier
  • Date: 2009-12-22 21:29:31 UTC
  • mfrom: (1.2.6 upstream) (4.1.2 squeeze)
  • Revision ID: james.westby@ubuntu.com-20091222212931-nhwkzapjwsmjao1l
Tags: 0.42.5-3
* debian/control:
  - add community site to homepage field
  - improve long description
  - remove Replaces and Conflicts fields
  - add Suggests on pd-csound, pd-pdp, pd-zexy, pd-aubio
* debian/rules: add per-arch configuration flags
* debian/patches/02_kfreebsd.diff:
  - also define pd_tilde_dllextent on FreeBSD
  - fix typo (really closing #414414 this time)
  - also add hurd glue
* debian/patches/04_hurd.diff:
  - add hurd glue and s_midi_dummy.c

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.  */
 
4
 
 
5
#include "m_pd.h"
 
6
#include "s_stuff.h"
 
7
#include <stdio.h>
 
8
 
 
9
#include <windows.h>
 
10
#include <MMSYSTEM.H>
 
11
 
 
12
    /* ------------- MIDI time stamping from audio clock ------------ */
 
13
 
 
14
#ifdef MIDI_TIMESTAMP
 
15
 
 
16
static double msw_hibuftime;
 
17
static double initsystime = -1;
 
18
 
 
19
    /* call this whenever we reset audio */
 
20
static void msw_resetmidisync(void)
 
21
{
 
22
    initsystime = clock_getsystime();
 
23
    msw_hibuftime = sys_getrealtime();
 
24
}
 
25
 
 
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. */
 
29
 
 
30
static void msw_midisync(void)
 
31
{
 
32
    double jittersec, diff;
 
33
    
 
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)
 
41
    {
 
42
        post("jitter excess %d %f", dac, diff);
 
43
        msw_resetmidisync();
 
44
    }
 
45
}
 
46
 
 
47
static double msw_midigettimefor(LARGE_INTEGER timestamp)
 
48
{
 
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);
 
53
}
 
54
#endif      /* MIDI_TIMESTAMP */
 
55
 
 
56
 
 
57
/* ------------------------- MIDI output -------------------------- */
 
58
static void msw_midiouterror(char *s, int err)
 
59
{
 
60
    char t[256];
 
61
    midiOutGetErrorText(err, t, 256);
 
62
    fprintf(stderr, s, t);
 
63
}
 
64
 
 
65
static HMIDIOUT hMidiOut[MAXMIDIOUTDEV];    /* output device */
 
66
static int msw_nmidiout;                            /* number of devices */
 
67
 
 
68
static void msw_open_midiout(int nmidiout, int *midioutvec)
 
69
{
 
70
    UINT result, wRtn;
 
71
    int i;
 
72
    int dev;
 
73
    MIDIOUTCAPS midioutcaps;
 
74
    if (nmidiout > MAXMIDIOUTDEV)
 
75
        nmidiout = MAXMIDIOUTDEV;
 
76
 
 
77
    dev = 0;
 
78
 
 
79
    for (i = 0; i < nmidiout; i++)
 
80
    {
 
81
        MIDIOUTCAPS mocap;
 
82
        int devno =  midioutvec[i];
 
83
        result = midiOutOpen(&hMidiOut[dev], devno, 0, 0, 
 
84
            CALLBACK_NULL);
 
85
        wRtn = midiOutGetDevCaps(i, (LPMIDIOUTCAPS) &mocap,
 
86
            sizeof(mocap));
 
87
        if (result != MMSYSERR_NOERROR)
 
88
        {
 
89
            fprintf(stderr,"midiOutOpen: %s\n",midioutcaps.szPname);
 
90
            msw_midiouterror("midiOutOpen: %s\n", result);
 
91
        }
 
92
        else
 
93
        {
 
94
            if (sys_verbose)
 
95
                fprintf(stderr,"midiOutOpen: Open %s as Port %d\n",
 
96
                    midioutcaps.szPname, dev);
 
97
            dev++;
 
98
        }
 
99
    }
 
100
    msw_nmidiout = dev;
 
101
}
 
102
 
 
103
static void msw_close_midiout(void)
 
104
{
 
105
    int i;
 
106
    for (i = 0; i < msw_nmidiout; i++)
 
107
    {
 
108
        midiOutReset(hMidiOut[i]);
 
109
        midiOutClose(hMidiOut[i]);
 
110
    }
 
111
    msw_nmidiout = 0;
 
112
}
 
113
 
 
114
/* -------------------------- MIDI input ---------------------------- */
 
115
 
 
116
#define INPUT_BUFFER_SIZE 1000     // size of input buffer in events
 
117
 
 
118
static void msw_midiinerror(char *s, int err)
 
119
{
 
120
    char t[256];
 
121
    midiInGetErrorText(err, t, 256);
 
122
    fprintf(stderr, s, t);
 
123
}
 
124
 
 
125
 
 
126
/* Structure to represent a single MIDI event.
 
127
 */
 
128
 
 
129
#define EVNT_F_ERROR    0x00000001L
 
130
 
 
131
typedef struct evemsw_tag
 
132
{
 
133
    DWORD fdwEvent;
 
134
    DWORD dwDevice;
 
135
    LARGE_INTEGER timestamp;
 
136
    DWORD data;
 
137
} EVENT;
 
138
typedef EVENT FAR *LPEVENT;
 
139
 
 
140
/* Structure to manage the circular input buffer.
 
141
 */
 
142
typedef struct circularBuffer_tag
 
143
{
 
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) */
 
153
} CIRCULARBUFFER;
 
154
typedef CIRCULARBUFFER FAR *LPCIRCULARBUFFER;
 
155
 
 
156
 
 
157
/* Structure to pass instance data from the application
 
158
   to the low-level callback function.
 
159
 */
 
160
typedef struct callbackInstance_tag
 
161
{
 
162
    HANDLE              hSelf;  
 
163
    DWORD               dwDevice;
 
164
    LPCIRCULARBUFFER    lpBuf;
 
165
} CALLBACKINSTANCEDATA;
 
166
typedef CALLBACKINSTANCEDATA FAR *LPCALLBACKINSTANCEDATA;
 
167
 
 
168
/* Function prototypes
 
169
 */
 
170
LPCALLBACKINSTANCEDATA FAR PASCAL AllocCallbackInstanceData(void);
 
171
void FAR PASCAL FreeCallbackInstanceData(LPCALLBACKINSTANCEDATA lpBuf);
 
172
 
 
173
LPCIRCULARBUFFER AllocCircularBuffer(DWORD dwSize);
 
174
void FreeCircularBuffer(LPCIRCULARBUFFER lpBuf);
 
175
WORD FAR PASCAL GetEvent(LPCIRCULARBUFFER lpBuf, LPEVENT lpEvent);
 
176
 
 
177
// Callback instance data pointers
 
178
LPCALLBACKINSTANCEDATA lpCallbackInstanceData[MAXMIDIINDEV];
 
179
 
 
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
 
184
 
 
185
LPCIRCULARBUFFER lpInputBuffer;         // Input buffer structure
 
186
EVENT incomingEvent;                    // Incoming MIDI event structure
 
187
 
 
188
MIDIINCAPS midiInCaps[MAXMIDIINDEV]; // Device capabilities structures
 
189
HMIDIIN hMidiIn[MAXMIDIINDEV];       // MIDI input device handles
 
190
 
 
191
 
 
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.
 
195
 *
 
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
 
199
 *      GlobalPageLock().
 
200
 *
 
201
 * Params:  void
 
202
 *
 
203
 * Return:  A pointer to the allocated CALLBACKINSTANCE data structure.
 
204
 */
 
205
LPCALLBACKINSTANCEDATA FAR PASCAL AllocCallbackInstanceData(void)
 
206
{
 
207
    HANDLE hMem;
 
208
    LPCALLBACKINSTANCEDATA lpBuf;
 
209
    
 
210
    /* Allocate and lock global memory.
 
211
     */
 
212
    hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE,
 
213
                       (DWORD)sizeof(CALLBACKINSTANCEDATA));
 
214
    if(hMem == NULL)
 
215
        return NULL;
 
216
    
 
217
    lpBuf = (LPCALLBACKINSTANCEDATA)GlobalLock(hMem);
 
218
    if(lpBuf == NULL){
 
219
        GlobalFree(hMem);
 
220
        return NULL;
 
221
    }
 
222
    
 
223
    /* Page lock the memory.
 
224
     */
 
225
    //GlobalPageLock((HGLOBAL)HIWORD(lpBuf));
 
226
 
 
227
    /* Save the handle.
 
228
     */
 
229
    lpBuf->hSelf = hMem;
 
230
 
 
231
    return lpBuf;
 
232
}
 
233
 
 
234
/* FreeCallbackInstanceData - Frees the given CALLBACKINSTANCEDATA structure.
 
235
 *
 
236
 * Params:  lpBuf - Points to the CALLBACKINSTANCEDATA structure to be freed.
 
237
 *
 
238
 * Return:  void
 
239
 */
 
240
void FAR PASCAL FreeCallbackInstanceData(LPCALLBACKINSTANCEDATA lpBuf)
 
241
{
 
242
    HANDLE hMem;
 
243
 
 
244
    /* Save the handle until we're through here.
 
245
     */
 
246
    hMem = lpBuf->hSelf;
 
247
 
 
248
    /* Free the structure.
 
249
     */
 
250
    //GlobalPageUnlock((HGLOBAL)HIWORD(lpBuf));
 
251
    GlobalUnlock(hMem);
 
252
    GlobalFree(hMem);
 
253
}
 
254
 
 
255
 
 
256
/*
 
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().
 
261
 *
 
262
 * Params:  dwSize - The size of the buffer, in events.
 
263
 *
 
264
 * Return:  A pointer to a CIRCULARBUFFER structure identifying the 
 
265
 *      allocated display buffer.  NULL if the buffer could not be allocated.
 
266
 */
 
267
 
 
268
 
 
269
LPCIRCULARBUFFER AllocCircularBuffer(DWORD dwSize)
 
270
{
 
271
    HANDLE hMem;
 
272
    LPCIRCULARBUFFER lpBuf;
 
273
    LPEVENT lpMem;
 
274
    
 
275
    /* Allocate and lock a CIRCULARBUFFER structure.
 
276
     */
 
277
    hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE,
 
278
                       (DWORD)sizeof(CIRCULARBUFFER));
 
279
    if(hMem == NULL)
 
280
        return NULL;
 
281
 
 
282
    lpBuf = (LPCIRCULARBUFFER)GlobalLock(hMem);
 
283
    if(lpBuf == NULL)
 
284
    {
 
285
        GlobalFree(hMem);
 
286
        return NULL;
 
287
    }
 
288
    
 
289
    /* Page lock the memory.  Global memory blocks accessed by
 
290
     * low-level callback functions must be page locked.
 
291
     */
 
292
#ifndef _WIN32
 
293
    GlobalSmartPageLock((HGLOBAL)HIWORD(lpBuf));
 
294
#endif
 
295
 
 
296
    /* Save the memory handle.
 
297
     */
 
298
    lpBuf->hSelf = hMem;
 
299
    
 
300
    /* Allocate and lock memory for the actual buffer.
 
301
     */
 
302
    hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, dwSize * sizeof(EVENT));
 
303
    if(hMem == NULL)
 
304
    {
 
305
#ifndef _WIN32
 
306
        GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf));
 
307
#endif
 
308
        GlobalUnlock(lpBuf->hSelf);
 
309
        GlobalFree(lpBuf->hSelf);
 
310
        return NULL;
 
311
    }
 
312
    
 
313
    lpMem = (LPEVENT)GlobalLock(hMem);
 
314
    if(lpMem == NULL)
 
315
    {
 
316
        GlobalFree(hMem);
 
317
#ifndef _WIN32
 
318
        GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf));
 
319
#endif
 
320
        GlobalUnlock(lpBuf->hSelf);
 
321
        GlobalFree(lpBuf->hSelf);
 
322
        return NULL;
 
323
    }
 
324
    
 
325
    /* Page lock the memory.  Global memory blocks accessed by
 
326
     * low-level callback functions must be page locked.
 
327
     */
 
328
#ifndef _WIN32
 
329
    GlobalSmartPageLock((HGLOBAL)HIWORD(lpMem));
 
330
#endif
 
331
    
 
332
    /* Set up the CIRCULARBUFFER structure.
 
333
     */
 
334
    lpBuf->hBuffer = hMem;
 
335
    lpBuf->wError = 0;
 
336
    lpBuf->dwSize = dwSize;
 
337
    lpBuf->dwCount = 0L;
 
338
    lpBuf->lpStart = lpMem;
 
339
    lpBuf->lpEnd = lpMem + dwSize;
 
340
    lpBuf->lpTail = lpMem;
 
341
    lpBuf->lpHead = lpMem;
 
342
        
 
343
    return lpBuf;
 
344
}
 
345
 
 
346
/* FreeCircularBuffer - Frees the memory for the given CIRCULARBUFFER 
 
347
 * structure and the memory for the buffer it references.
 
348
 *
 
349
 * Params:  lpBuf - Points to the CIRCULARBUFFER to be freed.
 
350
 *
 
351
 * Return:  void
 
352
 */
 
353
void FreeCircularBuffer(LPCIRCULARBUFFER lpBuf)
 
354
{
 
355
    HANDLE hMem;
 
356
    
 
357
    /* Free the buffer itself.
 
358
     */
 
359
#ifndef _WIN32
 
360
    GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf->lpStart));
 
361
#endif
 
362
    GlobalUnlock(lpBuf->hBuffer);
 
363
    GlobalFree(lpBuf->hBuffer);
 
364
    
 
365
    /* Free the CIRCULARBUFFER structure.
 
366
     */
 
367
    hMem = lpBuf->hSelf;
 
368
#ifndef _WIN32
 
369
    GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf));
 
370
#endif
 
371
    GlobalUnlock(hMem);
 
372
    GlobalFree(hMem);
 
373
}
 
374
 
 
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.
 
379
 *
 
380
 * Params:  lpBuf - Points to the circular buffer.
 
381
 *          lpEvent - Points to an EVENT structure that is filled with the
 
382
 *              retrieved event.
 
383
 *
 
384
 * Return:  Returns non-zero if successful, zero if there are no 
 
385
 *   events to get.
 
386
 */
 
387
WORD FAR PASCAL GetEvent(LPCIRCULARBUFFER lpBuf, LPEVENT lpEvent)
 
388
{
 
389
        /* If no event available, return */
 
390
    if (!wNumDevices || lpBuf->dwCount <= 0) return (0);
 
391
    
 
392
    /* Get the event.
 
393
     */
 
394
    *lpEvent = *lpBuf->lpTail;
 
395
    
 
396
    /* Decrement the byte count, bump the tail pointer.
 
397
     */
 
398
    --lpBuf->dwCount;
 
399
    ++lpBuf->lpTail;
 
400
    
 
401
    /* Wrap the tail pointer, if necessary.
 
402
     */
 
403
    if(lpBuf->lpTail >= lpBuf->lpEnd)
 
404
        lpBuf->lpTail = lpBuf->lpStart;
 
405
 
 
406
    return 1;
 
407
}
 
408
 
 
409
/* PutEvent - Puts an EVENT in a CIRCULARBUFFER.  If the buffer is full, 
 
410
 *      it sets the wError element of the CIRCULARBUFFER structure 
 
411
 *      to be non-zero.
 
412
 *
 
413
 * Params:  lpBuf - Points to the CIRCULARBUFFER.
 
414
 *          lpEvent - Points to the EVENT.
 
415
 *
 
416
 * Return:  void
 
417
*/
 
418
 
 
419
void FAR PASCAL PutEvent(LPCIRCULARBUFFER lpBuf, LPEVENT lpEvent)
 
420
{
 
421
    /* If the buffer is full, set an error and return. 
 
422
     */
 
423
    if(lpBuf->dwCount >= lpBuf->dwSize){
 
424
        lpBuf->wError = 1;
 
425
        return;
 
426
    }
 
427
    
 
428
    /* Put the event in the buffer, bump the head pointer and the byte count.
 
429
     */
 
430
    *lpBuf->lpHead = *lpEvent;
 
431
    
 
432
    ++lpBuf->lpHead;
 
433
    ++lpBuf->dwCount;
 
434
 
 
435
    /* Wrap the head pointer, if necessary.
 
436
     */
 
437
    if(lpBuf->lpHead >= lpBuf->lpEnd)
 
438
        lpBuf->lpHead = lpBuf->lpStart;
 
439
}
 
440
 
 
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.
 
445
 *
 
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().
 
450
 *      
 
451
 *
 
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)
 
457
 *
 
458
 * Return:  void
 
459
 */     
 
460
void FAR PASCAL midiInputHandler(
 
461
HMIDIIN hMidiIn, 
 
462
WORD wMsg, 
 
463
DWORD dwInstance, 
 
464
DWORD dwParam1, 
 
465
DWORD dwParam2)
 
466
{
 
467
    EVENT event;
 
468
    
 
469
    switch(wMsg)
 
470
    {
 
471
        case MIM_OPEN:
 
472
            break;
 
473
 
 
474
        /* The only error possible is invalid MIDI data, so just pass
 
475
         * the invalid data on so we'll see it.
 
476
         */
 
477
        case MIM_ERROR:
 
478
        case MIM_DATA:
 
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();
 
484
#endif
 
485
            /* Put the MIDI event in the circular input buffer.
 
486
             */
 
487
 
 
488
            PutEvent(((LPCALLBACKINSTANCEDATA)dwInstance)->lpBuf,
 
489
                       (LPEVENT) &event); 
 
490
 
 
491
            break;
 
492
 
 
493
        default:
 
494
            break;
 
495
    }
 
496
}
 
497
 
 
498
void msw_open_midiin(int nmidiin, int *midiinvec)
 
499
{
 
500
    UINT  wRtn;
 
501
    char szErrorText[256];
 
502
    unsigned int i;
 
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
 
506
     * application.
 
507
     */
 
508
    lpInputBuffer = AllocCircularBuffer((DWORD)(INPUT_BUFFER_SIZE));
 
509
    if (lpInputBuffer == NULL)
 
510
    {
 
511
        printf("Not enough memory available for input buffer.\n");
 
512
        return;
 
513
    }
 
514
 
 
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.
 
523
     */
 
524
    for (i=0; (i<(unsigned)nmidiin) && (i<MAXMIDIINDEV); i++)
 
525
    {
 
526
        if ((lpCallbackInstanceData[ndev] = AllocCallbackInstanceData()) == NULL)
 
527
        {
 
528
            printf("Not enough memory available.\n");
 
529
            FreeCircularBuffer(lpInputBuffer);
 
530
            return;
 
531
        }
 
532
        lpCallbackInstanceData[i]->dwDevice = i;
 
533
        lpCallbackInstanceData[i]->lpBuf = lpInputBuffer;
 
534
        
 
535
        wRtn = midiInOpen((LPHMIDIIN)&hMidiIn[ndev],
 
536
              midiinvec[i],
 
537
              (DWORD)midiInputHandler,
 
538
              (DWORD)lpCallbackInstanceData[ndev],
 
539
              CALLBACK_FUNCTION);
 
540
        if (wRtn)
 
541
        {
 
542
            FreeCallbackInstanceData(lpCallbackInstanceData[ndev]);
 
543
            msw_midiinerror("midiInOpen: %s\n", wRtn);
 
544
        }
 
545
        else ndev++;
 
546
    }
 
547
 
 
548
    /* Start MIDI input.
 
549
     */
 
550
    for (i=0; i<ndev; i++)
 
551
    {
 
552
        if (hMidiIn[i])
 
553
            midiInStart(hMidiIn[i]);
 
554
    }
 
555
    wNumDevices = ndev;
 
556
}
 
557
 
 
558
static void msw_close_midiin(void)
 
559
{
 
560
    unsigned int i;
 
561
    /* Stop, reset, close MIDI input.  Free callback instance data.
 
562
     */
 
563
 
 
564
    for (i=0; (i<wNumDevices) && (i<MAXMIDIINDEV); i++)
 
565
    {
 
566
        if (hMidiIn[i])
 
567
        {
 
568
            if (sys_verbose)
 
569
                post("closing MIDI input %d...", i);
 
570
            midiInStop(hMidiIn[i]);
 
571
            midiInReset(hMidiIn[i]);
 
572
            midiInClose(hMidiIn[i]);
 
573
            FreeCallbackInstanceData(lpCallbackInstanceData[i]);
 
574
        }
 
575
    }
 
576
    
 
577
    /* Free input buffer.
 
578
     */
 
579
    if (lpInputBuffer)
 
580
        FreeCircularBuffer(lpInputBuffer);
 
581
 
 
582
    if (sys_verbose)
 
583
        post("...done");
 
584
    wNumDevices = 0;
 
585
}
 
586
 
 
587
/* ------------------- public routines -------------------------- */
 
588
 
 
589
void sys_putmidimess(int portno, int a, int b, int c)
 
590
{
 
591
    DWORD foo;
 
592
    MMRESULT res;
 
593
    if (portno >= 0 && portno < msw_nmidiout)
 
594
    {
 
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);
 
599
    }
 
600
}
 
601
 
 
602
void sys_putmidibyte(int portno, int byte)
 
603
{
 
604
    MMRESULT res;
 
605
    if (portno >= 0 && portno < msw_nmidiout)
 
606
    {
 
607
        res = midiOutShortMsg(hMidiOut[portno], byte);
 
608
        if (res != MMSYSERR_NOERROR)
 
609
            post("MIDI out error %d", res);
 
610
    }
 
611
}
 
612
 
 
613
void sys_poll_midi(void)
 
614
{
 
615
    static EVENT msw_nextevent;
 
616
    static int msw_isnextevent;
 
617
    static double msw_nexteventtime;
 
618
 
 
619
    while (1)
 
620
    {
 
621
        if (!msw_isnextevent)
 
622
        {
 
623
            if (!GetEvent(lpInputBuffer, &msw_nextevent)) break;
 
624
            msw_isnextevent = 1;
 
625
#ifdef MIDI_TIMESTAMP
 
626
            msw_nexteventtime = msw_midigettimefor(&foo.timestamp);
 
627
#endif
 
628
        }
 
629
#ifdef MIDI_TIMESTAMP
 
630
        if (0.001 * clock_gettimesince(initsystime) >= msw_nexteventtime)
 
631
#endif
 
632
        {
 
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;
 
638
            switch (msgtype)
 
639
            {
 
640
            case 0: 
 
641
            case 1: 
 
642
            case 2:
 
643
            case 3:
 
644
            case 6:
 
645
                sys_midibytein(portno, commandbyte);
 
646
                sys_midibytein(portno, byte1);
 
647
                sys_midibytein(portno, byte2);
 
648
                break; 
 
649
            case 4:
 
650
            case 5:
 
651
                sys_midibytein(portno, commandbyte);
 
652
                sys_midibytein(portno, byte1);
 
653
                break;
 
654
            case 7:
 
655
                sys_midibytein(portno, commandbyte);
 
656
                break; 
 
657
            }
 
658
            msw_isnextevent = 0;
 
659
        }
 
660
    }
 
661
}
 
662
 
 
663
void sys_do_open_midi(int nmidiin, int *midiinvec,
 
664
    int nmidiout, int *midioutvec)
 
665
{
 
666
    if (nmidiout)
 
667
        msw_open_midiout(nmidiout, midioutvec);
 
668
    if (nmidiin)
 
669
    {
 
670
        post(
 
671
         "Warning: midi input is dangerous in Microsoft Windows; see Pd manual)");
 
672
        msw_open_midiin(nmidiin, midiinvec);
 
673
    }
 
674
}
 
675
 
 
676
void sys_close_midi( void)
 
677
{
 
678
    msw_close_midiin();
 
679
    msw_close_midiout();
 
680
}
 
681
 
 
682
#if 0
 
683
/* list the audio and MIDI device names */
 
684
void sys_listmididevs(void)
 
685
{
 
686
    UINT  wRtn, ndevices;
 
687
    unsigned int i;
 
688
 
 
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. */
 
691
 
 
692
    ndevices = midiInGetNumDevs();
 
693
    for (i = 0; i < ndevices; i++)
 
694
    {
 
695
        MIDIINCAPS micap;
 
696
        wRtn = midiInGetDevCaps(i, (LPMIDIINCAPS) &micap,
 
697
            sizeof(micap));
 
698
        if (wRtn) msw_midiinerror("midiInGetDevCaps: %s\n", wRtn);
 
699
        else fprintf(stderr,
 
700
            "MIDI input device #%d: %s\n", i+1, micap.szPname);
 
701
    }
 
702
 
 
703
    ndevices = midiOutGetNumDevs();
 
704
    for (i = 0; i < ndevices; i++)
 
705
    {
 
706
        MIDIOUTCAPS mocap;
 
707
        wRtn = midiOutGetDevCaps(i, (LPMIDIOUTCAPS) &mocap,
 
708
            sizeof(mocap));
 
709
        if (wRtn) msw_midiouterror("midiOutGetDevCaps: %s\n", wRtn);
 
710
        else fprintf(stderr,
 
711
            "MIDI output device #%d: %s\n", i+1, mocap.szPname);
 
712
    }
 
713
 
 
714
}
 
715
#endif
 
716
 
 
717
void midi_getdevs(char *indevlist, int *nindevs,
 
718
    char *outdevlist, int *noutdevs, int maxndev, int devdescsize)
 
719
{
 
720
    int i, nin = midiInGetNumDevs(), nout = midiOutGetNumDevs();
 
721
    UINT  wRtn;
 
722
    if (nin > maxndev)
 
723
        nin = maxndev;
 
724
    for (i = 0; i < nin; i++)
 
725
    {
 
726
        MIDIINCAPS micap;
 
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;
 
731
    }
 
732
    if (nout > maxndev)
 
733
        nout = maxndev;
 
734
    for (i = 0; i < nout; i++)
 
735
    {
 
736
        MIDIOUTCAPS mocap;
 
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;
 
741
    }
 
742
    *nindevs = nin;
 
743
    *noutdevs = nout;
 
744
}