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