/* * 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 /* Use THIS_FILE when it is available. */ #ifndef THIS_FILE #define THIS_FILE __FILE__ #endif #define MIDIOUT_CHECK_ERROR { \ if (err != MMSYSERR_NOERROR) \ ERROR3("MIDI OUT Error in %s:%d : %s\n", THIS_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; icount; 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; icount; 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