Path: blob/master/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_MidiOut.c
41149 views
/*1* Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#define USE_ERROR26#define USE_TRACE2728#include "PLATFORM_API_WinOS_Util.h"2930/* include to prevent charset problem */31#include "PLATFORM_API_WinOS_Charset_Util.h"3233#if USE_PLATFORM_MIDI_OUT == TRUE343536#ifdef USE_ERROR37#include <stdio.h>3839#define MIDIOUT_CHECK_ERROR { \40if (err != MMSYSERR_NOERROR) \41ERROR3("MIDI OUT Error in %s:%d : %s\n", __FILE__, __LINE__, MIDI_OUT_GetErrorStr((INT32) err)); \42}43#else44#define MIDIOUT_CHECK_ERROR45#endif4647/* *************************** MidiOutDeviceProvider implementation *********************************** */4849/* not thread safe */50static char winMidiOutErrMsg[WIN_MAX_ERROR_LEN];5152char* MIDI_OUT_GetErrorStr(INT32 err) {53winMidiOutErrMsg[0] = 0;54midiOutGetErrorText((MMRESULT) err, winMidiOutErrMsg, WIN_MAX_ERROR_LEN);55return winMidiOutErrMsg;56}5758INT32 MIDI_OUT_GetNumDevices() {59// add one for the MIDI_MAPPER60// we want to return it first so it'll be the default, so we61// decrement each deviceID for these methods....62return (INT32) (midiOutGetNumDevs() + 1);63}646566INT32 getMidiOutCaps(INT32 deviceID, MIDIOUTCAPSW* caps, INT32* err) {67UINT_PTR id;68if (deviceID == 0) {69id = MIDI_MAPPER;70} else {71id = (UINT_PTR)(deviceID-1);72}73(*err) = (INT32) midiOutGetDevCapsW(id, caps, sizeof(MIDIOUTCAPSW));74return ((*err) == MMSYSERR_NOERROR);75}767778INT32 MIDI_OUT_GetDeviceName(INT32 deviceID, char *name, UINT32 nameLength) {79MIDIOUTCAPSW midiOutCaps;80INT32 err;8182memset(&midiOutCaps, 0, sizeof(midiOutCaps));83if (getMidiOutCaps(deviceID, &midiOutCaps, &err)) {84UnicodeToUTF8AndCopy(name, midiOutCaps.szPname, nameLength);85return MIDI_SUCCESS;86}87MIDIOUT_CHECK_ERROR;88return err;89}909192INT32 MIDI_OUT_GetDeviceVendor(INT32 deviceID, char *name, UINT32 nameLength) {93return MIDI_NOT_SUPPORTED;94}959697INT32 MIDI_OUT_GetDeviceDescription(INT32 deviceID, char *name, UINT32 nameLength) {98MIDIOUTCAPSW midiOutCaps;99char *desc;100INT32 err;101102memset(&midiOutCaps, 0, sizeof(midiOutCaps));103if (getMidiOutCaps(deviceID, &midiOutCaps, &err)) {104int tech = (int)midiOutCaps.wTechnology;105switch(tech) {106case MOD_MIDIPORT:107desc = "External MIDI Port";108break;109case MOD_SQSYNTH:110desc = "Internal square wave synthesizer";111break;112case MOD_FMSYNTH:113desc = "Internal FM synthesizer";114break;115case MOD_SYNTH:116desc = "Internal synthesizer (generic)";117break;118case MOD_MAPPER:119desc = "Windows MIDI_MAPPER";120break;121case 7 /* MOD_SWSYNTH*/:122desc = "Internal software synthesizer";123break;124default:125return MIDI_NOT_SUPPORTED;126}127strncpy(name, desc, nameLength-1);128name[nameLength-1] = 0;129return MIDI_SUCCESS;130}131return err;132}133134135INT32 MIDI_OUT_GetDeviceVersion(INT32 deviceID, char *name, UINT32 nameLength) {136MIDIOUTCAPSW midiOutCaps;137INT32 err;138139memset(&midiOutCaps, 0, sizeof(midiOutCaps));140if (getMidiOutCaps(deviceID, &midiOutCaps, &err) && nameLength>7) {141sprintf(name, "%d.%d", (midiOutCaps.vDriverVersion & 0xFF00) >> 8, midiOutCaps.vDriverVersion & 0xFF);142return MIDI_SUCCESS;143}144MIDIOUT_CHECK_ERROR;145return err;146}147148149/* *************************** MidiOutDevice implementation ***************************************** */150151152INT32 unprepareLongBuffers(MidiDeviceHandle* handle) {153SysExQueue* sysex;154MMRESULT err = MMSYSERR_NOERROR;155int i;156157if (!handle || !handle->deviceHandle || !handle->longBuffers) {158ERROR0("MIDI_OUT_unprepareLongBuffers: handle, deviceHandle, or longBuffers == NULL\n");159return MIDI_INVALID_HANDLE;160}161sysex = (SysExQueue*) handle->longBuffers;162for (i = 0; i<sysex->count; i++) {163MIDIHDR* hdr = &(sysex->header[i]);164if (hdr->dwFlags) {165err = midiOutUnprepareHeader((HMIDIOUT) handle->deviceHandle, hdr, sizeof(MIDIHDR));166}167}168MIDIOUT_CHECK_ERROR;169return (INT32) err;170}171172INT32 freeLongBuffer(MIDIHDR* hdr, HMIDIOUT deviceHandle, INT32 minToLeaveData) {173MMRESULT err = MMSYSERR_NOERROR;174175if (!hdr) {176ERROR0("MIDI_OUT_freeLongBuffer: hdr == NULL\n");177return MIDI_INVALID_HANDLE;178}179if (hdr->dwFlags && deviceHandle) {180err = midiOutUnprepareHeader(deviceHandle, hdr, sizeof(MIDIHDR));181}182if (hdr->lpData && (((INT32) hdr->dwBufferLength) < minToLeaveData || minToLeaveData < 0)) {183free(hdr->lpData);184hdr->lpData=NULL;185hdr->dwBufferLength=0;186}187hdr->dwBytesRecorded=0;188hdr->dwFlags=0;189return (INT32) err;190}191192INT32 freeLongBuffers(MidiDeviceHandle* handle) {193SysExQueue* sysex;194MMRESULT err = MMSYSERR_NOERROR;195int i;196197if (!handle || !handle->longBuffers) {198ERROR0("MIDI_OUT_freeLongBuffers: handle or longBuffers == NULL\n");199return MIDI_INVALID_HANDLE;200}201sysex = (SysExQueue*) handle->longBuffers;202for (i = 0; i<sysex->count; i++) {203err = freeLongBuffer(&(sysex->header[i]), (HMIDIOUT) handle->deviceHandle, -1);204}205MIDIOUT_CHECK_ERROR;206return (INT32) err;207}208209INT32 MIDI_OUT_OpenDevice(INT32 deviceID, MidiDeviceHandle** handle) {210MMRESULT err;211212TRACE1(">> MIDI_OUT_OpenDevice: deviceID: %d\n", deviceID);213214if (deviceID == 0) {215deviceID = MIDI_MAPPER;216} else {217deviceID--;218}219#ifdef USE_ERROR220setvbuf(stdout, NULL, (int)_IONBF, 0);221setvbuf(stderr, NULL, (int)_IONBF, 0);222#endif223224(*handle) = (MidiDeviceHandle*) malloc(sizeof(MidiDeviceHandle));225if (!(*handle)) {226ERROR0("ERROR: MIDI_OUT_OpenDevice: out of memory\n");227return MIDI_OUT_OF_MEMORY;228}229memset(*handle, 0, sizeof(MidiDeviceHandle));230231// create long buffer queue232if (!MIDI_WinCreateEmptyLongBufferQueue(*handle, MIDI_OUT_LONG_QUEUE_SIZE)) {233ERROR0("ERROR: MIDI_OUT_OpenDevice: could not create long Buffers\n");234free(*handle);235(*handle) = NULL;236return MIDI_OUT_OF_MEMORY;237}238239// create notification event240(*handle)->platformData = (void*) CreateEvent(NULL, FALSE /*manual reset*/, FALSE /*signaled*/, NULL);241if (!(*handle)->platformData) {242ERROR0("ERROR: MIDI_OUT_StartDevice: could not create event\n");243MIDI_WinDestroyLongBufferQueue(*handle);244free(*handle);245(*handle) = NULL;246return MIDI_OUT_OF_MEMORY;247}248249// finally open the device250err = midiOutOpen((HMIDIOUT*) &((*handle)->deviceHandle), deviceID,251(UINT_PTR) (*handle)->platformData, (UINT_PTR) (*handle), CALLBACK_EVENT);252253if ((err != MMSYSERR_NOERROR) || (!(*handle)->deviceHandle)) {254/* some devices return non zero, but no error! */255if (midiOutShortMsg((HMIDIOUT) ((*handle)->deviceHandle),0) == MMSYSERR_INVALHANDLE) {256MIDIOUT_CHECK_ERROR;257CloseHandle((HANDLE) (*handle)->platformData);258MIDI_WinDestroyLongBufferQueue(*handle);259free(*handle);260(*handle) = NULL;261return (INT32) err;262}263}264//$$fb enable high resolution time265timeBeginPeriod(1);266MIDI_SetStartTime(*handle);267TRACE0("<< MIDI_OUT_OpenDevice: succeeded\n");268return MIDI_SUCCESS;269}270271INT32 MIDI_OUT_CloseDevice(MidiDeviceHandle* handle) {272MMRESULT err = MMSYSERR_NOERROR;273HANDLE event;274275TRACE0("> MIDI_OUT_CloseDevice\n");276if (!handle) {277ERROR0("ERROR: MIDI_OUT_StopDevice: handle is NULL\n");278return MIDI_INVALID_HANDLE; // failure279}280// encourage MIDI_OUT_SendLongMessage to return soon281event = handle->platformData;282handle->platformData = NULL;283if (event) {284SetEvent(event);285} else {286ERROR0("ERROR: MIDI_OUT_StopDevice: event is NULL\n");287}288289if (handle->deviceHandle) {290//$$fb disable high resolution time291timeEndPeriod(1);292err = midiOutReset((HMIDIOUT) handle->deviceHandle);293} else {294ERROR0("ERROR: MIDI_OUT_CloseDevice: deviceHandle is NULL\n");295}296297// issue a "SUSTAIN OFF" message to each MIDI channel, 0 to 15.298// "CONTROL CHANGE" is 176, "SUSTAIN CONTROLLER" is 64, and the value is 0.299// $$fb 2002-04-04: It is responsability of the application developer to300// leave the device in a consistent state. So I put this in comments301/*302for (channel = 0; channel < 16; channel++)303MIDI_OUT_SendShortMessage(deviceHandle, (unsigned char)(176 + channel), (unsigned char)64, (unsigned char)0, (UINT32)-1);304*/305306if (event) {307// wait until MIDI_OUT_SendLongMessage has finished308while (handle->isWaiting) Sleep(0);309}310311unprepareLongBuffers(handle);312313if (handle->deviceHandle) {314err = midiOutClose((HMIDIOUT) handle->deviceHandle);315MIDIOUT_CHECK_ERROR;316handle->deviceHandle = NULL;317}318freeLongBuffers(handle);319320if (event) {321CloseHandle(event);322}323MIDI_WinDestroyLongBufferQueue(handle);324free(handle);325326TRACE0("< MIDI_OUT_CloseDevice\n");327return (INT32) err;328}329330331/* return time stamp in microseconds */332INT64 MIDI_OUT_GetTimeStamp(MidiDeviceHandle* handle) {333return MIDI_GetTimeStamp(handle);334}335336337INT32 MIDI_OUT_SendShortMessage(MidiDeviceHandle* handle, UINT32 packedMsg, UINT32 timestamp) {338MMRESULT err = MMSYSERR_NOERROR;339340TRACE2("> MIDI_OUT_SendShortMessage %x, time: %d\n", packedMsg, timestamp);341if (!handle) {342ERROR0("ERROR: MIDI_OUT_SendShortMessage: handle is NULL\n");343return MIDI_INVALID_HANDLE; // failure344}345err = midiOutShortMsg((HMIDIOUT) handle->deviceHandle, packedMsg);346MIDIOUT_CHECK_ERROR;347TRACE0("< MIDI_OUT_SendShortMessage\n");348return (INT32) err;349}350351INT32 MIDI_OUT_SendLongMessage(MidiDeviceHandle* handle, UBYTE* data, UINT32 size, UINT32 timestamp) {352MMRESULT err;353SysExQueue* sysex;354MIDIHDR* hdr = NULL;355INT32 remainingSize;356int i;357358TRACE2("> MIDI_OUT_SendLongMessage size %d, time: %d\n", size, timestamp);359if (!handle || !data || !handle->longBuffers) {360ERROR0("< ERROR: MIDI_OUT_SendLongMessage: handle, data, or longBuffers is NULL\n");361return MIDI_INVALID_HANDLE; // failure362}363if (size == 0) {364return MIDI_SUCCESS;365}366367sysex = (SysExQueue*) handle->longBuffers;368remainingSize = size;369370// send in chunks of 512 bytes371size = 512;372while (remainingSize > 0) {373if (remainingSize < (INT32) size) {374size = (UINT32) remainingSize;375}376377while (!hdr && handle->platformData) {378/* find a non-queued header */379for (i = 0; i < sysex->count; i++) {380hdr = &(sysex->header[i]);381if ((hdr->dwFlags & MHDR_DONE) || (hdr->dwFlags == 0)) {382break;383}384hdr = NULL;385}386/* wait for a buffer to free up */387if (!hdr && handle->platformData) {388DWORD res;389TRACE0(" Need to wait for free buffer\n");390handle->isWaiting = TRUE;391res = WaitForSingleObject((HANDLE) handle->platformData, 700);392handle->isWaiting = FALSE;393if (res == WAIT_TIMEOUT) {394// break out back to Java if no buffer freed up after 700 milliseconds395TRACE0("-> TIMEOUT. Need to go back to Java\n");396break;397}398}399}400if (!hdr) {401// no free buffer402return MIDI_NOT_SUPPORTED;403}404405TRACE2("-> sending %d bytes with buffer index=%d\n", (int) size, (int) hdr->dwUser);406freeLongBuffer(hdr, handle->deviceHandle, (INT32) size);407if (hdr->lpData == NULL) {408hdr->lpData = malloc(size);409hdr->dwBufferLength = size;410}411hdr->dwBytesRecorded = size;412memcpy(hdr->lpData, data, size);413err = midiOutPrepareHeader((HMIDIOUT) handle->deviceHandle, hdr, sizeof(MIDIHDR));414if (err != MMSYSERR_NOERROR) {415freeLongBuffer(hdr, handle->deviceHandle, -1);416MIDIOUT_CHECK_ERROR;417return (INT32) err;418}419err = midiOutLongMsg((HMIDIOUT) handle->deviceHandle, hdr, sizeof(MIDIHDR));420if (err != MMSYSERR_NOERROR) {421freeLongBuffer(hdr, handle->deviceHandle, -1);422ERROR0("ERROR: MIDI_OUT_SendLongMessage: midiOutLongMsg returned error:\n");423MIDIOUT_CHECK_ERROR;424return (INT32) err;425}426remainingSize -= size;427data += size;428}429TRACE0("< MIDI_OUT_SendLongMessage success\n");430return MIDI_SUCCESS;431}432433#endif // USE_PLATFORM_MIDI_OUT434435436