Path: blob/master/src/java.desktop/windows/native/libjsound/PLATFORM_API_WinOS_MidiIn.cpp
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 Java Sound specific headers as C code */29extern "C" {30#include "PLATFORM_API_WinOS_Util.h"31}3233/* include to prevent charset problem */34#include "PLATFORM_API_WinOS_Charset_Util.h"3536#if USE_PLATFORM_MIDI_IN == TRUE3738#ifdef USE_ERROR39#include <stdio.h>4041#define MIDIIN_CHECK_ERROR { \42if (err != MMSYSERR_NOERROR) \43ERROR3("MIDI IN Error in %s:%d : %s\n", __FILE__, __LINE__, MIDI_IN_GetErrorStr((INT32) err)); \44}45#else46#define MIDIIN_CHECK_ERROR47#endif4849/*50* Callback from the MIDI device for all messages.51*/52//$$fb dwParam1 holds a pointer for long messages. How can that be a DWORD then ???53void CALLBACK MIDI_IN_PutMessage( HMIDIIN hMidiIn, UINT wMsg, UINT_PTR dwInstance, UINT_PTR dwParam1, UINT_PTR dwParam2 ) {5455MidiDeviceHandle* handle = (MidiDeviceHandle*) dwInstance;5657TRACE3("> MIDI_IN_PutMessage, hMidiIn: %x, wMsg: %x, dwInstance: %x\n", hMidiIn, wMsg, dwInstance);58TRACE2(" dwParam1: %x, dwParam2: %x\n", dwParam1, dwParam2);5960switch(wMsg) {6162case MIM_OPEN:63TRACE0("< MIDI_IN_PutMessage: MIM_OPEN\n");64break;6566case MIM_CLOSE:67TRACE0("< MIDI_IN_PutMessage: MIM_CLOSE\n");68break;6970case MIM_MOREDATA:71case MIM_DATA:72TRACE3(" MIDI_IN_PutMessage: MIM_MOREDATA or MIM_DATA. status=%x data1=%x data2=%x\n",73dwParam1 & 0xFF, (dwParam1 & 0xFF00)>>8, (dwParam1 & 0xFF0000)>>16);74if (handle!=NULL && handle->queue!=NULL && handle->platformData) {75MIDI_QueueAddShort(handle->queue,76// queue stores packedMsg in big endian77//(dwParam1 << 24) | ((dwParam1 << 8) & 0xFF0000) | ((dwParam1 >> 8) & 0xFF00),78(UINT32) dwParam1,79// queue uses microseconds80((INT64) dwParam2)*1000,81// overwrite if queue is full82TRUE);83SetEvent((HANDLE) handle->platformData);84}85TRACE0("< MIDI_IN_PutMessage\n");86break;8788case MIM_LONGDATA:89TRACE1(" MIDI_IN_PutMessage: MIM_LONGDATA (%d bytes recorded)\n", (int) (((MIDIHDR*) dwParam1)->dwBytesRecorded));90if (handle!=NULL && handle->queue!=NULL && handle->platformData) {91MIDIHDR* hdr = (MIDIHDR*) dwParam1;92TRACE2(" MIDI_IN_PutMessage: Adding to queue: index %d, %d bytes\n", (INT32) hdr->dwUser, hdr->dwBytesRecorded);93MIDI_QueueAddLong(handle->queue,94(UBYTE*) hdr->lpData,95(UINT32) hdr->dwBytesRecorded,96// sysex buffer index97(INT32) hdr->dwUser,98// queue uses microseconds99((INT64) dwParam2)*1000,100// overwrite if queue is full101TRUE);102SetEvent((HANDLE) handle->platformData);103}104TRACE0("< MIDI_IN_PutMessage\n");105break;106107case MIM_ERROR:108ERROR0("< MIDI_IN_PutMessage: MIM_ERROR!\n");109break;110111case MIM_LONGERROR:112if (dwParam1 != 0) {113MIDIHDR* hdr = (MIDIHDR*) dwParam1;114#ifdef USE_TRACE115if (hdr->dwBytesRecorded > 0) {116TRACE2(" MIDI_IN_PutMessage: MIM_LONGERROR! recorded: %d bytes with status 0x%2x\n",117hdr->dwBytesRecorded, (int) (*((UBYTE*) hdr->lpData)));118}119#endif120// re-add hdr to device query121hdr->dwBytesRecorded = 0;122midiInAddBuffer((HMIDIIN)handle->deviceHandle, hdr, sizeof(MIDIHDR));123}124ERROR0("< MIDI_IN_PutMessage: MIM_LONGERROR!\n");125break;126127default:128ERROR1("< MIDI_IN_PutMessage: ERROR unknown message %d!\n", wMsg);129break;130131} // switch (wMsg)132}133134135/*136** data/routines for opening MIDI input (MidiIn) device by separate thread137** (joint into MidiIn_OpenHelper class)138** see 6415669 - MidiIn device stops work and crushes JVM after exiting139** from thread that has open the device (it looks like WinMM bug).140*/141class MidiIn_OpenHelper {142public:143/* opens MidiIn device */144static MMRESULT midiInOpen(INT32 deviceID, MidiDeviceHandle* handle);145/* checks for initialization success */146static inline BOOL isInitialized() { return data.threadHandle != NULL; }147protected:148MidiIn_OpenHelper() {} // no need to create an instance149150/* data class */151class Data {152public:153Data();154~Data();155// public data to access from parent class156CRITICAL_SECTION crit_sect;157volatile HANDLE threadHandle;158volatile HANDLE doEvent; // event to resume thread159volatile HANDLE doneEvent; // processing has been completed160volatile MMRESULT err; // processing result161// data to process; (handle == null) is command to thread terminating162volatile INT32 deviceID;163volatile MidiDeviceHandle* handle;164} static data;165166/* StartThread function */167static DWORD WINAPI __stdcall ThreadProc(void *param);168};169170/* MidiIn_OpenHelper class implementation171*/172MidiIn_OpenHelper::Data MidiIn_OpenHelper::data;173174MidiIn_OpenHelper::Data::Data() {175threadHandle = NULL;176::InitializeCriticalSection(&crit_sect);177doEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);178doneEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);179if (doEvent != NULL && doneEvent != NULL)180threadHandle = ::CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);181}182183MidiIn_OpenHelper::Data::~Data() {184::EnterCriticalSection(&crit_sect);185if (threadHandle != NULL) {186// terminate thread187handle = NULL;188::SetEvent(doEvent);189::CloseHandle(threadHandle);190threadHandle = NULL;191}192::LeaveCriticalSection(&crit_sect);193// won't delete doEvent/doneEvent/crit_sect194// - Windows will do during process shutdown195}196197DWORD WINAPI __stdcall MidiIn_OpenHelper::ThreadProc(void *param) {198while (1) {199// wait for something to do200::WaitForSingleObject(data.doEvent, INFINITE);201if (data.handle == NULL) {202// (data.handle == NULL) is a signal to terminate thread203break;204}205206data.err = ::midiInOpen((HMIDIIN*)&(data.handle->deviceHandle),207data.deviceID, (UINT_PTR)&(MIDI_IN_PutMessage),208(UINT_PTR)data.handle,209CALLBACK_FUNCTION|MIDI_IO_STATUS);210211::SetEvent(data.doneEvent);212}213return 0;214}215216MMRESULT MidiIn_OpenHelper::midiInOpen(INT32 deviceID, MidiDeviceHandle* handle) {217MMRESULT err;218::EnterCriticalSection(&data.crit_sect);219if (!isInitialized()) {220::LeaveCriticalSection(&data.crit_sect);221return MMSYSERR_ERROR;222}223data.deviceID = deviceID;224data.handle = handle;225::SetEvent(data.doEvent);226::WaitForSingleObject(data.doneEvent, INFINITE);227err = data.err;228::LeaveCriticalSection(&data.crit_sect);229return err;230}231232233// PLATFORM_MIDI_IN method implementations234235/* not thread safe */236static char winMidiInErrMsg[WIN_MAX_ERROR_LEN];237238char* MIDI_IN_GetErrorStr(INT32 err) {239winMidiInErrMsg[0] = 0;240midiInGetErrorText((MMRESULT) err, winMidiInErrMsg, WIN_MAX_ERROR_LEN);241return winMidiInErrMsg;242}243244INT32 MIDI_IN_GetNumDevices() {245return (INT32) midiInGetNumDevs();246}247248INT32 getMidiInCaps(INT32 deviceID, MIDIINCAPSW* caps, INT32* err) {249(*err) = midiInGetDevCapsW(deviceID, caps, sizeof(MIDIINCAPSW));250return ((*err) == MMSYSERR_NOERROR);251}252253INT32 MIDI_IN_GetDeviceName(INT32 deviceID, char *name, UINT32 nameLength) {254MIDIINCAPSW midiInCaps;255INT32 err;256257memset(&midiInCaps, 0, sizeof(midiInCaps));258if (getMidiInCaps(deviceID, &midiInCaps, &err)) {259UnicodeToUTF8AndCopy(name, midiInCaps.szPname, nameLength);260return MIDI_SUCCESS;261}262MIDIIN_CHECK_ERROR;263return err;264}265266267INT32 MIDI_IN_GetDeviceVendor(INT32 deviceID, char *name, UINT32 nameLength) {268return MIDI_NOT_SUPPORTED;269}270271272INT32 MIDI_IN_GetDeviceDescription(INT32 deviceID, char *name, UINT32 nameLength) {273return MIDI_NOT_SUPPORTED;274}275276277278INT32 MIDI_IN_GetDeviceVersion(INT32 deviceID, char *name, UINT32 nameLength) {279MIDIINCAPSW midiInCaps;280INT32 err = MIDI_NOT_SUPPORTED;281282memset(&midiInCaps, 0, sizeof(midiInCaps));283if (getMidiInCaps(deviceID, &midiInCaps, &err) && (nameLength>7)) {284sprintf(name, "%d.%d", (midiInCaps.vDriverVersion & 0xFF00) >> 8, midiInCaps.vDriverVersion & 0xFF);285return MIDI_SUCCESS;286}287MIDIIN_CHECK_ERROR;288return err;289}290291292INT32 prepareBuffers(MidiDeviceHandle* handle) {293SysExQueue* sysex;294MMRESULT err = MMSYSERR_NOERROR;295int i;296297if (!handle || !handle->longBuffers || !handle->deviceHandle) {298ERROR0("MIDI_IN_prepareBuffers: handle, or longBuffers, or deviceHandle==NULL\n");299return MIDI_INVALID_HANDLE;300}301sysex = (SysExQueue*) handle->longBuffers;302for (i = 0; i<sysex->count; i++) {303MIDIHDR* hdr = &(sysex->header[i]);304midiInPrepareHeader((HMIDIIN) handle->deviceHandle, hdr, sizeof(MIDIHDR));305err = midiInAddBuffer((HMIDIIN) handle->deviceHandle, hdr, sizeof(MIDIHDR));306}307MIDIIN_CHECK_ERROR;308return (INT32) err;309}310311INT32 unprepareBuffers(MidiDeviceHandle* handle) {312SysExQueue* sysex;313MMRESULT err = MMSYSERR_NOERROR;314int i;315316if (!handle || !handle->longBuffers || !handle->deviceHandle) {317ERROR0("MIDI_IN_unprepareBuffers: handle, or longBuffers, or deviceHandle==NULL\n");318return MIDI_INVALID_HANDLE;319}320sysex = (SysExQueue*) handle->longBuffers;321for (i = 0; i<sysex->count; i++) {322err = midiInUnprepareHeader((HMIDIIN) handle->deviceHandle, &(sysex->header[i]), sizeof(MIDIHDR));323}324MIDIIN_CHECK_ERROR;325return (INT32) err;326}327328INT32 MIDI_IN_OpenDevice(INT32 deviceID, MidiDeviceHandle** handle) {329MMRESULT err;330331TRACE0("> MIDI_IN_OpenDevice\n");332#ifdef USE_ERROR333setvbuf(stdout, NULL, (int)_IONBF, 0);334setvbuf(stderr, NULL, (int)_IONBF, 0);335#endif336337(*handle) = (MidiDeviceHandle*) malloc(sizeof(MidiDeviceHandle));338if (!(*handle)) {339ERROR0("< ERROR: MIDI_IN_OpenDevice: out of memory\n");340return MIDI_OUT_OF_MEMORY;341}342memset(*handle, 0, sizeof(MidiDeviceHandle));343344// create queue345(*handle)->queue = MIDI_CreateQueue(MIDI_IN_MESSAGE_QUEUE_SIZE);346if (!(*handle)->queue) {347ERROR0("< ERROR: MIDI_IN_OpenDevice: could not create queue\n");348free(*handle);349(*handle) = NULL;350return MIDI_OUT_OF_MEMORY;351}352353// create long buffer queue354if (!MIDI_WinCreateLongBufferQueue(*handle, MIDI_IN_LONG_QUEUE_SIZE, MIDI_IN_LONG_MESSAGE_SIZE, NULL)) {355ERROR0("< ERROR: MIDI_IN_OpenDevice: could not create long Buffers\n");356MIDI_DestroyQueue((*handle)->queue);357free(*handle);358(*handle) = NULL;359return MIDI_OUT_OF_MEMORY;360}361362// finally open the device363err = MidiIn_OpenHelper::midiInOpen(deviceID, *handle);364365if ((err != MMSYSERR_NOERROR) || (!(*handle)->deviceHandle)) {366MIDIIN_CHECK_ERROR;367MIDI_WinDestroyLongBufferQueue(*handle);368MIDI_DestroyQueue((*handle)->queue);369free(*handle);370(*handle) = NULL;371return (INT32) err;372}373374prepareBuffers(*handle);375MIDI_SetStartTime(*handle);376TRACE0("< MIDI_IN_OpenDevice: midiInOpen succeeded\n");377return MIDI_SUCCESS;378}379380381INT32 MIDI_IN_CloseDevice(MidiDeviceHandle* handle) {382MMRESULT err;383384TRACE0("> MIDI_IN_CloseDevice: midiInClose\n");385if (!handle) {386ERROR0("ERROR: MIDI_IN_CloseDevice: handle is NULL\n");387return MIDI_INVALID_HANDLE;388}389midiInReset((HMIDIIN) handle->deviceHandle);390unprepareBuffers(handle);391err = midiInClose((HMIDIIN) handle->deviceHandle);392handle->deviceHandle=NULL;393MIDIIN_CHECK_ERROR;394MIDI_WinDestroyLongBufferQueue(handle);395396if (handle->queue!=NULL) {397MidiMessageQueue* queue = handle->queue;398handle->queue = NULL;399MIDI_DestroyQueue(queue);400}401free(handle);402403TRACE0("< MIDI_IN_CloseDevice: midiInClose succeeded\n");404return (INT32) err;405}406407408INT32 MIDI_IN_StartDevice(MidiDeviceHandle* handle) {409MMRESULT err;410411if (!handle || !handle->deviceHandle || !handle->queue) {412ERROR0("ERROR: MIDI_IN_StartDevice: handle or queue is NULL\n");413return MIDI_INVALID_HANDLE;414}415416// clear all the events from the queue417MIDI_QueueClear(handle->queue);418419handle->platformData = (void*) CreateEvent(NULL, FALSE /*manual reset*/, FALSE /*signaled*/, NULL);420if (!handle->platformData) {421ERROR0("ERROR: MIDI_IN_StartDevice: could not create event\n");422return MIDI_OUT_OF_MEMORY;423}424425err = midiInStart((HMIDIIN) handle->deviceHandle);426/* $$mp 200308-11: This method is already called in ...open(). It is427unclear why it is called again. The specification says that428MidiDevice.getMicrosecondPosition() returns the time since the429device was opened (the spec doesn't know about start/stop).430So I guess this call is obsolete. */431MIDI_SetStartTime(handle);432433MIDIIN_CHECK_ERROR;434TRACE0("MIDI_IN_StartDevice: midiInStart finished\n");435return (INT32) err;436}437438439INT32 MIDI_IN_StopDevice(MidiDeviceHandle* handle) {440MMRESULT err;441HANDLE event;442443TRACE0("> MIDI_IN_StopDevice: midiInStop \n");444if (!handle || !handle->platformData) {445ERROR0("ERROR: MIDI_IN_StopDevice: handle or event is NULL\n");446return MIDI_INVALID_HANDLE;447}448// encourage MIDI_IN_GetMessage to return soon449event = handle->platformData;450handle->platformData = NULL;451SetEvent(event);452453err = midiInStop((HMIDIIN) handle->deviceHandle);454455// wait until the Java thread has exited456while (handle->isWaiting) Sleep(0);457CloseHandle(event);458459MIDIIN_CHECK_ERROR;460TRACE0("< MIDI_IN_StopDevice: midiInStop finished\n");461return (INT32) err;462}463464465/* return time stamp in microseconds */466INT64 MIDI_IN_GetTimeStamp(MidiDeviceHandle* handle) {467return MIDI_GetTimeStamp(handle);468}469470471// read the next message from the queue472MidiMessage* MIDI_IN_GetMessage(MidiDeviceHandle* handle) {473if (handle == NULL) {474return NULL;475}476while (handle->queue!=NULL && handle->platformData!=NULL) {477MidiMessage* msg = MIDI_QueueRead(handle->queue);478DWORD res;479if (msg != NULL) {480//fprintf(stdout, "GetMessage returns index %d\n", msg->data.l.index); fflush(stdout);481return msg;482}483TRACE0("MIDI_IN_GetMessage: before waiting\n");484handle->isWaiting = TRUE;485res = WaitForSingleObject((HANDLE) handle->platformData, 2000);486handle->isWaiting = FALSE;487if (res == WAIT_TIMEOUT) {488// break out back to Java from time to time - just to be sure489TRACE0("MIDI_IN_GetMessage: waiting finished with timeout\n");490break;491}492TRACE0("MIDI_IN_GetMessage: waiting finished\n");493}494return NULL;495}496497void MIDI_IN_ReleaseMessage(MidiDeviceHandle* handle, MidiMessage* msg) {498SysExQueue* sysex;499if (handle == NULL || handle->queue == NULL) {500return;501}502sysex = (SysExQueue*) handle->longBuffers;503if (msg->type == LONG_MESSAGE && sysex) {504MIDIHDR* hdr = &(sysex->header[msg->data.l.index]);505//fprintf(stdout, "ReleaseMessage index %d\n", msg->data.l.index); fflush(stdout);506hdr->dwBytesRecorded = 0;507midiInAddBuffer((HMIDIIN) handle->deviceHandle, hdr, sizeof(MIDIHDR));508}509MIDI_QueueRemove(handle->queue, TRUE /*onlyLocked*/);510}511512#endif // USE_PLATFORM_MIDI_IN513514515