1/*
2 * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26//#define USE_ERROR
27//#define USE_TRACE
28
29/* Use THIS_FILE when it is available. */
30#ifndef THIS_FILE
31    #define THIS_FILE __FILE__
32#endif
33
34#if USE_PLATFORM_MIDI_OUT == TRUE
35
36#include "PLATFORM_API_MacOSX_MidiUtils.h"
37
38char* MIDI_OUT_GetErrorStr(INT32 err) {
39    return (char *) MIDI_Utils_GetErrorMsg((int) err);
40}
41
42
43INT32 MIDI_OUT_GetNumDevices() {
44    return MIDI_Utils_GetNumDevices(MIDI_OUT);
45}
46
47
48INT32 MIDI_OUT_GetDeviceName(INT32 deviceID, char *name, UINT32 nameLength) {
49    return MIDI_Utils_GetDeviceName(MIDI_OUT, deviceID, name, nameLength);
50}
51
52
53INT32 MIDI_OUT_GetDeviceVendor(INT32 deviceID, char *name, UINT32 nameLength) {
54    return MIDI_Utils_GetDeviceVendor(MIDI_OUT, deviceID, name, nameLength);
55}
56
57
58INT32 MIDI_OUT_GetDeviceDescription(INT32 deviceID, char *name, UINT32 nameLength) {
59    return MIDI_Utils_GetDeviceDescription(MIDI_OUT, deviceID, name, nameLength);
60}
61
62
63INT32 MIDI_OUT_GetDeviceVersion(INT32 deviceID, char *name, UINT32 nameLength) {
64    return MIDI_Utils_GetDeviceVersion(MIDI_OUT, deviceID, name, nameLength);
65}
66
67
68/* *************************** MidiOutDevice implementation ***************************************** */
69
70INT32 MIDI_OUT_OpenDevice(INT32 deviceID, MidiDeviceHandle** handle) {
71    TRACE1("MIDI_OUT_OpenDevice: deviceID: %d\n", (int) deviceID);
72    /* queue sizes are ignored for MIDI_OUT only (uses STREAMS) */
73    return MIDI_Utils_OpenDevice(MIDI_OUT, deviceID, (MacMidiDeviceHandle**) handle, 0, 0, 0);
74}
75
76INT32 MIDI_OUT_CloseDevice(MidiDeviceHandle* handle) {
77    TRACE0("MIDI_OUT_CloseDevice\n");
78
79    // issue a "SUSTAIN OFF" message to each MIDI channel, 0 to 15.
80    // "CONTROL CHANGE" is 176, "SUSTAIN CONTROLLER" is 64, and the value is 0.
81    // $$fb 2002-04-04: It is responsability of the application developer to
82    // leave the device in a consistent state. So I put this in comments
83    /*
84      for (channel = 0; channel < 16; channel++)
85      MIDI_OUT_SendShortMessage(deviceHandle, (unsigned char)(176 + channel),
86      (unsigned char)64, (unsigned char)0, (UINT32)-1);
87    */
88    return MIDI_Utils_CloseDevice((MacMidiDeviceHandle*) handle);
89}
90
91
92INT64 MIDI_OUT_GetTimeStamp(MidiDeviceHandle* handle) {
93    return MIDI_Utils_GetTimeStamp((MacMidiDeviceHandle*) handle);
94}
95
96
97INT32 MIDI_OUT_SendShortMessage(MidiDeviceHandle* handle, UINT32 packedMsg, UINT32 timestamp) {
98    OSStatus err = noErr;
99
100    TRACE2("> MIDI_OUT_SendShortMessage %x, time: %d\n", (uint) packedMsg, (int) timestamp);
101    if (!handle) {
102        ERROR0("< ERROR: MIDI_OUT_SendShortMessage: handle is NULL\n");
103        return MIDI_INVALID_HANDLE;
104    }
105
106    MacMidiDeviceHandle* macHandle = (MacMidiDeviceHandle*) handle;
107    UInt8 mBuffers[100];
108    MIDIPacketList* packetList = (MIDIPacketList*) mBuffers;
109    MIDIPacket* packet;
110    UINT32 nData;
111    Byte data[3] = {packedMsg & 0xFF, (packedMsg >> 8) & 0xFF, (packedMsg >> 16) & 0xFF};
112    bool byteIsInvalid = FALSE;
113
114    packet = MIDIPacketListInit(packetList);
115    switch (data[0] & 0xF0) {
116        case 0x80:    // Note off
117        case 0x90:    // Note on
118        case 0xA0:    // Aftertouch
119        case 0xB0:    // Controller
120        case 0xE0:    // Pitch wheel
121            nData = 3;
122            break;
123
124        case 0xC0:    // Program change
125        case 0xD0:    // Channel pressure
126            nData = 2;
127            break;
128
129        case 0xF0: {
130            // System common message
131            switch (data[0]) {
132                case 0xF0:
133                case 0xF7:
134                    // System exclusive
135                    fprintf(stderr, "%s: %d->internal error: sysex message status=0x%X while sending short message\n",
136                            THIS_FILE, __LINE__, data[0]);
137                    byteIsInvalid = TRUE;
138                    break;
139
140                case 0xF1:    // MTC quarter frame message
141                    //fprintf(stderr, ">>>MIDI_OUT_SendShortMessage: MTC quarter frame message....\n");
142                    nData = 2;
143                    break;
144                case 0xF3:    // Song select
145                    //fprintf(stderr, ">>>MIDI_OUT_SendShortMessage: Song select....\n");
146                    nData = 2;
147                    break;
148
149                case 0xF2:    // Song position pointer
150                    //fprintf(stderr, ">>>MIDI_OUT_SendShortMessage: Song position pointer....\n");
151                    nData = 3;
152                    break;
153
154                case 0xF6:    // Tune request
155                    //fprintf(stderr, ">>>MIDI_OUT_SendShortMessage: Tune request....\n");
156                    nData = 1;
157                    break;
158
159                default:
160                    // Invalid message
161                    fprintf(stderr, "%s: %d->Invalid message: message status=0x%X while sending short message\n",
162                            THIS_FILE, __LINE__, data[0]);
163                    byteIsInvalid = TRUE;
164                    break;
165            }
166            break;
167        }
168
169        default:
170            // This can't happen, but handle it anyway.
171            fprintf(stderr, "%s: %d->Invalid message: message status=0x%X while sending short message\n",
172                    THIS_FILE, __LINE__, data[0]);
173            byteIsInvalid = TRUE;
174            break;
175    }
176
177    if (byteIsInvalid) return -1;
178
179    MIDIPacketListAdd(packetList, sizeof(mBuffers), packet, 0, nData, data);
180    err = MIDISend(macHandle->port, (MIDIEndpointRef) (intptr_t) handle->deviceHandle, packetList);
181
182    MIDI_CHECK_ERROR;
183    TRACE0("< MIDI_OUT_SendShortMessage\n");
184    return (err == noErr ? MIDI_SUCCESS : -1);
185}
186
187
188INT32 MIDI_OUT_SendLongMessage(MidiDeviceHandle* handle, UBYTE* data, UINT32 size, UINT32 timestamp) {
189    OSStatus err = noErr;
190
191    TRACE2("> MIDI_OUT_SendLongMessage size %d, time: %d\n", (int) size, (int) timestamp);
192    if (!handle || !data) {
193        ERROR0("< ERROR: MIDI_OUT_SendLongMessage: handle, or data is NULL\n");
194        return MIDI_INVALID_HANDLE;
195    }
196    if (size == 0) {
197        return MIDI_SUCCESS;
198    }
199
200    MacMidiDeviceHandle* macHandle = (MacMidiDeviceHandle*) handle;
201    UInt8 mBuffers[8196];
202    MIDIPacketList* packetList = (MIDIPacketList*) mBuffers;
203    MIDIPacket* packet = NULL;
204    UINT32 remaining = size;
205    UINT32 increment = 512;
206    UINT32 nData;
207
208    handle->isWaiting = TRUE;
209
210    while (remaining > 0) {
211
212        if (packet == NULL) {
213            packet = MIDIPacketListInit(packetList);
214        }
215
216        if (remaining > increment) {
217            nData = increment;
218        } else {
219            nData = remaining;
220        }
221
222        // Copies the bytes to our current packet.
223        if ((packet = MIDIPacketListAdd(packetList, sizeof(mBuffers), packet, 0, nData, (const Byte*) data)) == NULL) {
224            // Packet list is full, send it.
225            err = MIDISend(macHandle->port, (MIDIEndpointRef) (intptr_t) handle->deviceHandle, packetList);
226            if (err != noErr) {
227                break;
228            }
229        } else {
230            // Moves the data pointer to the next segment.
231            data += nData;
232            remaining -= nData;
233            packet = MIDIPacketNext(packet);
234        }
235    }
236
237    MIDI_CHECK_ERROR;
238    handle->isWaiting = FALSE;
239    TRACE0("< MIDI_OUT_SendLongMessage\n");
240    return (err == noErr ? MIDI_SUCCESS : -1);
241}
242
243#endif /* USE_PLATFORM_MIDI_OUT */
244