1 /*
2  * Copyright (c) 1999, 2019, 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 package com.sun.media.sound;
27 
28 import javax.sound.midi.MidiUnavailableException;
29 import javax.sound.midi.Transmitter;
30 
31 /**
32  * MidiInDevice class representing functionality of MidiIn devices.
33  *
34  * @author David Rivas
35  * @author Kara Kytle
36  * @author Florian Bomers
37  */
38 final class MidiInDevice extends AbstractMidiDevice implements Runnable {
39 
40     private volatile Thread midiInThread;
41 
MidiInDevice(AbstractMidiDeviceProvider.Info info)42     MidiInDevice(AbstractMidiDeviceProvider.Info info) {
43         super(info);
44     }
45 
46     // $$kk: 06.24.99: i have this both opening and starting the midi in device.
47     // may want to separate these??
48     @Override
implOpen()49     protected synchronized void implOpen() throws MidiUnavailableException {
50         int index = ((MidiInDeviceProvider.MidiInDeviceInfo)getDeviceInfo()).getIndex();
51         id = nOpen(index); // can throw MidiUnavailableException
52 
53         if (id == 0) {
54             throw new MidiUnavailableException("Unable to open native device");
55         }
56 
57         // create / start a thread to get messages
58         if (midiInThread == null) {
59             midiInThread = JSSecurityManager.createThread(this,
60                                                     "Java Sound MidiInDevice Thread",   // name
61                                                     false,  // daemon
62                                                     -1,    // priority
63                                                     true); // doStart
64         }
65 
66         nStart(id); // can throw MidiUnavailableException
67     }
68 
69     // $$kk: 06.24.99: i have this both stopping and closing the midi in device.
70     // may want to separate these??
71     @Override
implClose()72     protected synchronized void implClose() {
73         long oldId = id;
74         id = 0;
75 
76         super.implClose();
77 
78         // close the device
79         nStop(oldId);
80         if (midiInThread != null) {
81             try {
82                 midiInThread.join(1000);
83             } catch (InterruptedException e) {
84                 // IGNORE EXCEPTION
85             }
86         }
87         nClose(oldId);
88     }
89 
90     @Override
getMicrosecondPosition()91     public long getMicrosecondPosition() {
92         long timestamp = -1;
93         if (isOpen()) {
94             timestamp = nGetTimeStamp(id);
95         }
96         return timestamp;
97     }
98 
99     // OVERRIDES OF ABSTRACT MIDI DEVICE METHODS
100 
101     @Override
hasTransmitters()102     protected boolean hasTransmitters() {
103         return true;
104     }
105 
106     @Override
createTransmitter()107     protected Transmitter createTransmitter() {
108         return new MidiInTransmitter();
109     }
110 
111     /**
112       * An own class to distinguish the class name from
113       * the transmitter of other devices.
114       */
115     private final class MidiInTransmitter extends BasicTransmitter {
MidiInTransmitter()116         private MidiInTransmitter() {
117             super();
118         }
119     }
120 
121     @Override
run()122     public void run() {
123         // while the device is started, keep trying to get messages.
124         // this thread returns from native code whenever stop() or close() is called
125         while (id!=0) {
126             // go into native code and retrieve messages
127             nGetMessages(id);
128             if (id!=0) {
129                 try {
130                     Thread.sleep(1);
131                 } catch (InterruptedException e) {}
132             }
133         }
134         // let the thread exit
135         midiInThread = null;
136     }
137 
138     /**
139      * Callback from native code when a short MIDI event is received from hardware.
140      * @param packedMsg: status | data1 << 8 | data2 << 8
141      * @param timeStamp time-stamp in microseconds
142      */
callbackShortMessage(int packedMsg, long timeStamp)143     void callbackShortMessage(int packedMsg, long timeStamp) {
144         if (packedMsg == 0 || id == 0) {
145             return;
146         }
147 
148         /*if(Printer.verbose) {
149           int status = packedMsg & 0xFF;
150           int data1 = (packedMsg & 0xFF00)>>8;
151           int data2 = (packedMsg & 0xFF0000)>>16;
152           Printer.verbose(">> MidiInDevice callbackShortMessage: status: " + status + " data1: " + data1 + " data2: " + data2 + " timeStamp: " + timeStamp);
153           }*/
154 
155         getTransmitterList().sendMessage(packedMsg, timeStamp);
156     }
157 
callbackLongMessage(byte[] data, long timeStamp)158     void callbackLongMessage(byte[] data, long timeStamp) {
159         if (id == 0 || data == null) {
160             return;
161         }
162         getTransmitterList().sendMessage(data, timeStamp);
163     }
164 
nOpen(int index)165     private native long nOpen(int index) throws MidiUnavailableException;
nClose(long id)166     private native void nClose(long id);
167 
nStart(long id)168     private native void nStart(long id) throws MidiUnavailableException;
nStop(long id)169     private native void nStop(long id);
nGetTimeStamp(long id)170     private native long nGetTimeStamp(long id);
171 
172     // go into native code and get messages. May be blocking
nGetMessages(long id)173     private native void nGetMessages(long id);
174 }
175