blob: e4e68b7675430e4e067bbd5366f739f23c343f07 [file] [log] [blame]
/*
* Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#define USE_ERROR
#define USE_TRACE
#include "PLATFORM_API_WinOS_Util.h"
#if USE_PLATFORM_MIDI_OUT == TRUE
#ifdef USE_ERROR
#include <stdio.h>
#define MIDIOUT_CHECK_ERROR { \
if (err != MMSYSERR_NOERROR) \
ERROR3("MIDI OUT Error in %s:%d : %s\n", __FILE__, __LINE__, MIDI_OUT_GetErrorStr((INT32) err)); \
}
#else
#define MIDIOUT_CHECK_ERROR
#endif
/* *************************** MidiOutDeviceProvider implementation *********************************** */
/* not thread safe */
static char winMidiOutErrMsg[WIN_MAX_ERROR_LEN];
char* MIDI_OUT_GetErrorStr(INT32 err) {
winMidiOutErrMsg[0] = 0;
midiOutGetErrorText((MMRESULT) err, winMidiOutErrMsg, WIN_MAX_ERROR_LEN);
return winMidiOutErrMsg;
}
INT32 MIDI_OUT_GetNumDevices() {
// add one for the MIDI_MAPPER
// we want to return it first so it'll be the default, so we
// decrement each deviceID for these methods....
return (INT32) (midiOutGetNumDevs() + 1);
}
INT32 getMidiOutCaps(INT32 deviceID, MIDIOUTCAPS* caps, INT32* err) {
if (deviceID == 0) {
deviceID = MIDI_MAPPER;
} else {
deviceID--;
}
(*err) = (INT32) midiOutGetDevCaps(deviceID, caps, sizeof(MIDIOUTCAPS));
return ((*err) == MMSYSERR_NOERROR);
}
INT32 MIDI_OUT_GetDeviceName(INT32 deviceID, char *name, UINT32 nameLength) {
MIDIOUTCAPS midiOutCaps;
INT32 err;
if (getMidiOutCaps(deviceID, &midiOutCaps, &err)) {
strncpy(name, midiOutCaps.szPname, nameLength-1);
name[nameLength-1] = 0;
return MIDI_SUCCESS;
}
MIDIOUT_CHECK_ERROR;
return err;
}
INT32 MIDI_OUT_GetDeviceVendor(INT32 deviceID, char *name, UINT32 nameLength) {
return MIDI_NOT_SUPPORTED;
}
INT32 MIDI_OUT_GetDeviceDescription(INT32 deviceID, char *name, UINT32 nameLength) {
MIDIOUTCAPS midiOutCaps;
char *desc;
INT32 err;
if (getMidiOutCaps(deviceID, &midiOutCaps, &err)) {
int tech = (int)midiOutCaps.wTechnology;
switch(tech) {
case MOD_MIDIPORT:
desc = "External MIDI Port";
break;
case MOD_SQSYNTH:
desc = "Internal square wave synthesizer";
break;
case MOD_FMSYNTH:
desc = "Internal FM synthesizer";
break;
case MOD_SYNTH:
desc = "Internal synthesizer (generic)";
break;
case MOD_MAPPER:
desc = "Windows MIDI_MAPPER";
break;
case 7 /* MOD_SWSYNTH*/:
desc = "Internal software synthesizer";
break;
default:
return MIDI_NOT_SUPPORTED;
}
strncpy(name, desc, nameLength-1);
name[nameLength-1] = 0;
return MIDI_SUCCESS;
}
return err;
}
INT32 MIDI_OUT_GetDeviceVersion(INT32 deviceID, char *name, UINT32 nameLength) {
MIDIOUTCAPS midiOutCaps;
INT32 err;
if (getMidiOutCaps(deviceID, &midiOutCaps, &err) && nameLength>7) {
sprintf(name, "%d.%d", (midiOutCaps.vDriverVersion & 0xFF00) >> 8, midiOutCaps.vDriverVersion & 0xFF);
return MIDI_SUCCESS;
}
MIDIOUT_CHECK_ERROR;
return err;
}
/* *************************** MidiOutDevice implementation ***************************************** */
INT32 unprepareLongBuffers(MidiDeviceHandle* handle) {
SysExQueue* sysex;
MMRESULT err = MMSYSERR_NOERROR;
int i;
if (!handle || !handle->deviceHandle || !handle->longBuffers) {
ERROR0("MIDI_OUT_unprepareLongBuffers: handle, deviceHandle, or longBuffers == NULL\n");
return MIDI_INVALID_HANDLE;
}
sysex = (SysExQueue*) handle->longBuffers;
for (i = 0; i<sysex->count; i++) {
MIDIHDR* hdr = &(sysex->header[i]);
if (hdr->dwFlags) {
err = midiOutUnprepareHeader((HMIDIOUT) handle->deviceHandle, hdr, sizeof(MIDIHDR));
}
}
MIDIOUT_CHECK_ERROR;
return (INT32) err;
}
INT32 freeLongBuffer(MIDIHDR* hdr, HMIDIOUT deviceHandle, INT32 minToLeaveData) {
MMRESULT err = MMSYSERR_NOERROR;
if (!hdr) {
ERROR0("MIDI_OUT_freeLongBuffer: hdr == NULL\n");
return MIDI_INVALID_HANDLE;
}
if (hdr->dwFlags && deviceHandle) {
err = midiOutUnprepareHeader(deviceHandle, hdr, sizeof(MIDIHDR));
}
if (hdr->lpData && (((INT32) hdr->dwBufferLength) < minToLeaveData || minToLeaveData < 0)) {
free(hdr->lpData);
hdr->lpData=NULL;
hdr->dwBufferLength=0;
}
hdr->dwBytesRecorded=0;
hdr->dwFlags=0;
return (INT32) err;
}
INT32 freeLongBuffers(MidiDeviceHandle* handle) {
SysExQueue* sysex;
MMRESULT err = MMSYSERR_NOERROR;
int i;
if (!handle || !handle->longBuffers) {
ERROR0("MIDI_OUT_freeLongBuffers: handle or longBuffers == NULL\n");
return MIDI_INVALID_HANDLE;
}
sysex = (SysExQueue*) handle->longBuffers;
for (i = 0; i<sysex->count; i++) {
err = freeLongBuffer(&(sysex->header[i]), (HMIDIOUT) handle->deviceHandle, -1);
}
MIDIOUT_CHECK_ERROR;
return (INT32) err;
}
INT32 MIDI_OUT_OpenDevice(INT32 deviceID, MidiDeviceHandle** handle) {
MMRESULT err;
TRACE1(">> MIDI_OUT_OpenDevice: deviceID: %d\n", deviceID);
if (deviceID == 0) {
deviceID = MIDI_MAPPER;
} else {
deviceID--;
}
#ifdef USE_ERROR
setvbuf(stdout, NULL, (int)_IONBF, 0);
setvbuf(stderr, NULL, (int)_IONBF, 0);
#endif
(*handle) = (MidiDeviceHandle*) malloc(sizeof(MidiDeviceHandle));
if (!(*handle)) {
ERROR0("ERROR: MIDI_OUT_OpenDevice: out of memory\n");
return MIDI_OUT_OF_MEMORY;
}
memset(*handle, 0, sizeof(MidiDeviceHandle));
// create long buffer queue
if (!MIDI_WinCreateEmptyLongBufferQueue(*handle, MIDI_OUT_LONG_QUEUE_SIZE)) {
ERROR0("ERROR: MIDI_OUT_OpenDevice: could not create long Buffers\n");
free(*handle);
(*handle) = NULL;
return MIDI_OUT_OF_MEMORY;
}
// create notification event
(*handle)->platformData = (void*) CreateEvent(NULL, FALSE /*manual reset*/, FALSE /*signaled*/, NULL);
if (!(*handle)->platformData) {
ERROR0("ERROR: MIDI_OUT_StartDevice: could not create event\n");
MIDI_WinDestroyLongBufferQueue(*handle);
free(*handle);
(*handle) = NULL;
return MIDI_OUT_OF_MEMORY;
}
// finally open the device
err = midiOutOpen((HMIDIOUT*) &((*handle)->deviceHandle), deviceID,
(UINT_PTR) (*handle)->platformData, (UINT_PTR) (*handle), CALLBACK_EVENT);
if ((err != MMSYSERR_NOERROR) || (!(*handle)->deviceHandle)) {
/* some devices return non zero, but no error! */
if (midiOutShortMsg((HMIDIOUT) ((*handle)->deviceHandle),0) == MMSYSERR_INVALHANDLE) {
MIDIOUT_CHECK_ERROR;
CloseHandle((HANDLE) (*handle)->platformData);
MIDI_WinDestroyLongBufferQueue(*handle);
free(*handle);
(*handle) = NULL;
return (INT32) err;
}
}
//$$fb enable high resolution time
timeBeginPeriod(1);
MIDI_SetStartTime(*handle);
TRACE0("<< MIDI_OUT_OpenDevice: succeeded\n");
return MIDI_SUCCESS;
}
INT32 MIDI_OUT_CloseDevice(MidiDeviceHandle* handle) {
MMRESULT err = MMSYSERR_NOERROR;
HANDLE event;
TRACE0("> MIDI_OUT_CloseDevice\n");
if (!handle) {
ERROR0("ERROR: MIDI_OUT_StopDevice: handle is NULL\n");
return MIDI_INVALID_HANDLE; // failure
}
// encourage MIDI_OUT_SendLongMessage to return soon
event = handle->platformData;
handle->platformData = NULL;
if (event) {
SetEvent(event);
} else {
ERROR0("ERROR: MIDI_OUT_StopDevice: event is NULL\n");
}
if (handle->deviceHandle) {
//$$fb disable high resolution time
timeEndPeriod(1);
err = midiOutReset((HMIDIOUT) handle->deviceHandle);
} else {
ERROR0("ERROR: MIDI_OUT_CloseDevice: deviceHandle is NULL\n");
}
// issue a "SUSTAIN OFF" message to each MIDI channel, 0 to 15.
// "CONTROL CHANGE" is 176, "SUSTAIN CONTROLLER" is 64, and the value is 0.
// $$fb 2002-04-04: It is responsability of the application developer to
// leave the device in a consistent state. So I put this in comments
/*
for (channel = 0; channel < 16; channel++)
MIDI_OUT_SendShortMessage(deviceHandle, (unsigned char)(176 + channel), (unsigned char)64, (unsigned char)0, (UINT32)-1);
*/
if (event) {
// wait until MIDI_OUT_SendLongMessage has finished
while (handle->isWaiting) Sleep(0);
}
unprepareLongBuffers(handle);
if (handle->deviceHandle) {
err = midiOutClose((HMIDIOUT) handle->deviceHandle);
MIDIOUT_CHECK_ERROR;
handle->deviceHandle = NULL;
}
freeLongBuffers(handle);
if (event) {
CloseHandle(event);
}
MIDI_WinDestroyLongBufferQueue(handle);
free(handle);
TRACE0("< MIDI_OUT_CloseDevice\n");
return (INT32) err;
}
/* return time stamp in microseconds */
INT64 MIDI_OUT_GetTimeStamp(MidiDeviceHandle* handle) {
return MIDI_GetTimeStamp(handle);
}
INT32 MIDI_OUT_SendShortMessage(MidiDeviceHandle* handle, UINT32 packedMsg, UINT32 timestamp) {
MMRESULT err = MMSYSERR_NOERROR;
TRACE2("> MIDI_OUT_SendShortMessage %x, time: %d\n", packedMsg, timestamp);
if (!handle) {
ERROR0("ERROR: MIDI_OUT_SendShortMessage: handle is NULL\n");
return MIDI_INVALID_HANDLE; // failure
}
err = midiOutShortMsg((HMIDIOUT) handle->deviceHandle, packedMsg);
MIDIOUT_CHECK_ERROR;
TRACE0("< MIDI_OUT_SendShortMessage\n");
return (INT32) err;
}
INT32 MIDI_OUT_SendLongMessage(MidiDeviceHandle* handle, UBYTE* data, UINT32 size, UINT32 timestamp) {
MMRESULT err;
SysExQueue* sysex;
MIDIHDR* hdr = NULL;
INT32 remainingSize;
int i;
TRACE2("> MIDI_OUT_SendLongMessage size %d, time: %d\n", size, timestamp);
if (!handle || !data || !handle->longBuffers) {
ERROR0("< ERROR: MIDI_OUT_SendLongMessage: handle, data, or longBuffers is NULL\n");
return MIDI_INVALID_HANDLE; // failure
}
if (size == 0) {
return MIDI_SUCCESS;
}
sysex = (SysExQueue*) handle->longBuffers;
remainingSize = size;
// send in chunks of 512 bytes
size = 512;
while (remainingSize > 0) {
if (remainingSize < (INT32) size) {
size = (UINT32) remainingSize;
}
while (!hdr && handle->platformData) {
/* find a non-queued header */
for (i = 0; i < sysex->count; i++) {
hdr = &(sysex->header[i]);
if ((hdr->dwFlags & MHDR_DONE) || (hdr->dwFlags == 0)) {
break;
}
hdr = NULL;
}
/* wait for a buffer to free up */
if (!hdr && handle->platformData) {
DWORD res;
TRACE0(" Need to wait for free buffer\n");
handle->isWaiting = TRUE;
res = WaitForSingleObject((HANDLE) handle->platformData, 700);
handle->isWaiting = FALSE;
if (res == WAIT_TIMEOUT) {
// break out back to Java if no buffer freed up after 700 milliseconds
TRACE0("-> TIMEOUT. Need to go back to Java\n");
break;
}
}
}
if (!hdr) {
// no free buffer
return MIDI_NOT_SUPPORTED;
}
TRACE2("-> sending %d bytes with buffer index=%d\n", (int) size, (int) hdr->dwUser);
freeLongBuffer(hdr, handle->deviceHandle, (INT32) size);
if (hdr->lpData == NULL) {
hdr->lpData = malloc(size);
hdr->dwBufferLength = size;
}
hdr->dwBytesRecorded = size;
memcpy(hdr->lpData, data, size);
err = midiOutPrepareHeader((HMIDIOUT) handle->deviceHandle, hdr, sizeof(MIDIHDR));
if (err != MMSYSERR_NOERROR) {
freeLongBuffer(hdr, handle->deviceHandle, -1);
MIDIOUT_CHECK_ERROR;
return (INT32) err;
}
err = midiOutLongMsg((HMIDIOUT) handle->deviceHandle, hdr, sizeof(MIDIHDR));
if (err != MMSYSERR_NOERROR) {
freeLongBuffer(hdr, handle->deviceHandle, -1);
ERROR0("ERROR: MIDI_OUT_SendLongMessage: midiOutLongMsg returned error:\n");
MIDIOUT_CHECK_ERROR;
return (INT32) err;
}
remainingSize -= size;
data += size;
}
TRACE0("< MIDI_OUT_SendLongMessage success\n");
return MIDI_SUCCESS;
}
#endif // USE_PLATFORM_MIDI_OUT