aboutsummaryrefslogtreecommitdiff
path: root/desiredata/portmidi_osx/portmidi.c
blob: c8883303adda2cbdcaf2f976d29eb2c9542f0725 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
#include "stdlib.h"
#include "portmidi.h"
#include "pminternal.h"

#define is_empty(midi) ((midi)->tail == (midi)->head)

static int pm_initialized = FALSE;

int descriptor_index = 0;
descriptor_node descriptors[pm_descriptor_max];


/* pm_add_device -- describe interface/device pair to library 
 *
 * This is called at initialization time, once for each 
 * interface (e.g. DirectSound) and device (e.g. SoundBlaster 1)
 * The strings are retained but NOT COPIED, so do not destroy them!
 *
 * returns pmInvalidDeviceId if device memory is exceeded
 * otherwise returns pmNoError
 *
 */

PmError pm_add_device(char *interf, char *name, int input, 
                      void *descriptor, pm_fns_type dictionary)
{
    if (descriptor_index >= pm_descriptor_max) {
        return pmInvalidDeviceId;
    }
    descriptors[descriptor_index].pub.interf = interf;
    descriptors[descriptor_index].pub.name = name;
    descriptors[descriptor_index].pub.input = input;
    descriptors[descriptor_index].pub.output = !input;
    descriptors[descriptor_index].descriptor = descriptor;
    descriptors[descriptor_index].dictionary = dictionary;
    descriptor_index++;
    return pmNoError;
}


PmError Pm_Initialize( void )
{
    if (!pm_initialized) {
        PmError err = pm_init(); /* defined by implementation specific file */
        if (err) return err;
        pm_initialized = TRUE;
    }
    return pmNoError;
}


PmError Pm_Terminate( void )
{
    PmError err = pmNoError;
    if (pm_initialized) {
        err = pm_term(); /* defined by implementation specific file */
        /* note that even when pm_term() fails, we mark portmidi as
           not initialized */
        pm_initialized = FALSE;
    }
    return err;
}


int Pm_CountDevices( void )
{
    PmError err = Pm_Initialize();
    if (err) return err;

    return descriptor_index;
}


const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id )
{
    PmError err = Pm_Initialize();
    if (err) return NULL;

    if (id >= 0 && id < descriptor_index) {
        return &descriptors[id].pub;
    }
    return NULL;
}


/* failure_fn -- "noop" function pointer */
/**/
PmError failure_fn(PmInternal *midi)
{
    return pmBadPtr;
}


/* pm_success_fn -- "noop" function pointer */
/**/
PmError pm_success_fn(PmInternal *midi)
{
    return pmNoError;
}


PmError none_write(PmInternal *midi, PmEvent *buffer, long length)
{
    return length; /* if we return 0, caller might get into a loop */
}

PmError pm_fail_fn(PmInternal *midi)
{
    return pmBadPtr;
}

static PmError none_open(PmInternal *midi, void *driverInfo)
{
    return pmBadPtr;
}

#define none_abort pm_fail_fn

#define none_close pm_fail_fn


pm_fns_node pm_none_dictionary = {
    none_write, none_open,
    none_abort, none_close };


/* Pm_Read -- read up to length longs from source into buffer */
/*
 * returns number of longs actually read, or error code
 When the reader wants data:
    if overflow_flag:
        do not get anything
        empty the buffer (read_ptr = write_ptr)
        clear overflow_flag
        return pmBufferOverflow
    get data
    return number of messages


 */
PmError Pm_Read( PortMidiStream *stream, PmEvent *buffer, long length)
{
    PmInternal *midi = (PmInternal *) stream;
    int n = 0;
    long head = midi->head;
    while (head != midi->tail && n < length) {
        *buffer++ = midi->buffer[head++];
        if (head == midi->buffer_len) head = 0;
        n++;
    }
    midi->head = head;
    if (midi->overflow) {
        midi->head = midi->tail;
        midi->overflow = FALSE;
        return pmBufferOverflow;
    }
    return n;
}


PmError Pm_Poll( PortMidiStream *stream )
{
    PmInternal *midi = (PmInternal *) stream;
    return midi->head != midi->tail;
}


PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, long length)
{
    PmInternal *midi = (PmInternal *) stream;
    return (*midi->dictionary->write)(midi, buffer, length);
}


PmError Pm_WriteShort( PortMidiStream *stream, long when, long msg)
{
    PmEvent event;
    event.timestamp = when;
    event.message = msg;
    return Pm_Write(stream, &event, 1);
}


PmError Pm_OpenInput( PortMidiStream** stream,
                PmDeviceID inputDevice,
                void *inputDriverInfo,
                long bufferSize,
                PmTimeProcPtr time_proc,
                void *time_info,
                PmStream *thru)
{
    PmInternal *midi;

    PmError err = Pm_Initialize();
    if (err) return err;

    if (inputDevice < 0 || inputDevice >= descriptor_index) {
        return pmInvalidDeviceId;
    }

    if (!descriptors[inputDevice].pub.input) {
        return pmInvalidDeviceId;
    }

    midi = (PmInternal *) malloc(sizeof(PmInternal));
    *stream = midi;             
    if (!midi) return pmInsufficientMemory;

    midi->head = 0;
    midi->tail = 0;
    midi->dictionary = &pm_none_dictionary;
    midi->overflow = FALSE;
    midi->flush = FALSE;
    midi->sysex_in_progress = FALSE;
    midi->buffer_len = bufferSize;
    midi->buffer = (PmEvent *) pm_alloc(sizeof(PmEvent) * midi->buffer_len);
    if (!midi->buffer) return pmInsufficientMemory;
    midi->latency = 0;
    midi->thru = thru;
    midi->time_proc = time_proc;
    midi->time_info = time_info;
    midi->device_id = inputDevice;
    midi->dictionary = descriptors[inputDevice].dictionary;
    midi->write_flag = FALSE;
    err = (*midi->dictionary->open)(midi, inputDriverInfo);
    if (err) {
        pm_free(midi->buffer);      
        *stream = NULL;
    }
    return err;
}


PmError Pm_OpenOutput( PortMidiStream** stream,
                       PmDeviceID outputDevice,
                       void *outputDriverInfo,
                       long bufferSize,
                       PmTimeProcPtr time_proc,
                       void *time_info,
                       long latency )
{
    PmInternal *midi;

    PmError err = Pm_Initialize();
    if (err) return err;

    if (outputDevice < 0 || outputDevice >= descriptor_index) {
        return pmInvalidDeviceId;
    }

    if (!descriptors[outputDevice].pub.output) {
        return pmInvalidDeviceId;
    }

    midi = (PmInternal *) pm_alloc(sizeof(PmInternal));
    *stream = midi;                 
    if (!midi) return pmInsufficientMemory;

    midi->head = 0;
    midi->tail = 0;
    midi->buffer_len = bufferSize;
    midi->buffer = NULL;
    midi->device_id = outputDevice;
    midi->dictionary = descriptors[outputDevice].dictionary;
    midi->time_proc = time_proc;
    midi->time_info = time_info;
    midi->latency = latency;
    midi->write_flag = TRUE;
    err = (*midi->dictionary->open)(midi, outputDriverInfo);
    if (err) {
        *stream = NULL;
        pm_free(midi); // Fixed by Ning Hu, Sep.2001
    }
    return err;
}


PmError Pm_Abort( PortMidiStream* stream )
{
    PmInternal *midi = (PmInternal *) stream;
    return (*midi->dictionary->abort)(midi);
}

PmError Pm_Close( PortMidiStream *stream )
{
    PmInternal *midi = (PmInternal *) stream;
    return (*midi->dictionary->close)(midi);
}


const char *Pm_GetErrorText( PmError errnum )
{
    const char *msg;

    switch(errnum)
    {
    case pmNoError:                  msg = "Success"; break;
    case pmHostError:                msg = "Host error."; break;
    case pmInvalidDeviceId:          msg = "Invalid device ID."; break;
    case pmInsufficientMemory:       msg = "Insufficient memory."; break;
    case pmBufferTooSmall:           msg = "Buffer too small."; break;
    case pmBadPtr:                   msg = "Bad pointer."; break;
    case pmInternalError:            msg = "Internal PortMidi Error."; break;
    default:                         msg = "Illegal error number."; break;
    }
    return msg;
}


long pm_next_time(PmInternal *midi)
{
    return midi->buffer[midi->head].timestamp;
}


/* source should not enqueue data if overflow is set */
/*
  When producer has data to enqueue:
    if buffer is full:
        set overflow_flag and flush_flag
        return
    else if overflow_flag:
        return
    else if flush_flag:
        if sysex message is in progress:
            return
        else:
            clear flush_flag
            // fall through to enqueue data
    enqueue the data

 */
void pm_enqueue(PmInternal *midi, PmEvent *event)
{
    long tail = midi->tail;
    midi->buffer[tail++] = *event;
    if (tail == midi->buffer_len) tail = 0;
    if (tail == midi->head || midi->overflow) {
        midi->overflow = TRUE;
        midi->flush = TRUE;
        return;
    }
    if (midi->flush) {
        if (midi->sysex_in_progress) return;
        else midi->flush = FALSE;
    }
    midi->tail = tail;
}


int pm_queue_full(PmInternal *midi)
{
    long tail = midi->tail + 1;
    if (tail == midi->buffer_len) tail = 0;
    return tail == midi->head;
}