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.MidiMessage;
29 import javax.sound.midi.MidiUnavailableException;
30 import javax.sound.midi.Receiver;
31 import javax.sound.midi.ShortMessage;
32 
33 /**
34  * MidiOutDevice class representing functionality of MidiOut devices.
35  *
36  * @author David Rivas
37  * @author Kara Kytle
38  * @author Florian Bomers
39  */
40 final class MidiOutDevice extends AbstractMidiDevice {
41 
MidiOutDevice(AbstractMidiDeviceProvider.Info info)42     MidiOutDevice(AbstractMidiDeviceProvider.Info info) {
43         super(info);
44     }
45 
46     @Override
implOpen()47     protected synchronized void implOpen() throws MidiUnavailableException {
48         int index = ((AbstractMidiDeviceProvider.Info)getDeviceInfo()).getIndex();
49         id = nOpen(index); // can throw MidiUnavailableException
50         if (id == 0) {
51             throw new MidiUnavailableException("Unable to open native device");
52         }
53     }
54 
55     @Override
implClose()56     protected synchronized void implClose() {
57         // prevent further action
58         long oldId = id;
59         id = 0;
60 
61         super.implClose();
62 
63         // close the device
64         nClose(oldId);
65     }
66 
67     @Override
getMicrosecondPosition()68     public long getMicrosecondPosition() {
69         long timestamp = -1;
70         if (isOpen()) {
71             timestamp = nGetTimeStamp(id);
72         }
73         return timestamp;
74     }
75 
76     /** Returns if this device supports Receivers.
77         This implementation always returns true.
78         @return true, if the device supports Receivers, false otherwise.
79     */
80     @Override
hasReceivers()81     protected boolean hasReceivers() {
82         return true;
83     }
84 
85     @Override
createReceiver()86     protected Receiver createReceiver() {
87         return new MidiOutReceiver();
88     }
89 
90     final class MidiOutReceiver extends AbstractReceiver {
91 
92         @Override
implSend(final MidiMessage message, final long timeStamp)93         void implSend(final MidiMessage message, final long timeStamp) {
94             final int length = message.getLength();
95             final int status = message.getStatus();
96             if (length <= 3 && status != 0xF0 && status != 0xF7) {
97                 int packedMsg;
98                 if (message instanceof ShortMessage) {
99                     if (message instanceof FastShortMessage) {
100                         packedMsg = ((FastShortMessage) message).getPackedMsg();
101                     } else {
102                         ShortMessage msg = (ShortMessage) message;
103                         packedMsg = (status & 0xFF)
104                             | ((msg.getData1() & 0xFF) << 8)
105                             | ((msg.getData2() & 0xFF) << 16);
106                     }
107                 } else {
108                     packedMsg = 0;
109                     byte[] data = message.getMessage();
110                     if (length>0) {
111                         packedMsg = data[0] & 0xFF;
112                         if (length>1) {
113                             /* We handle meta messages here. The message
114                                system reset (FF) doesn't get until here,
115                                because it's length is only 1. So if we see
116                                a status byte of FF, it's sure that we
117                                have a Meta message. */
118                             if (status == 0xFF) {
119                                 return;
120                             }
121                             packedMsg |= (data[1] & 0xFF) << 8;
122                             if (length>2) {
123                                 packedMsg |= (data[2] & 0xFF) << 16;
124                             }
125                         }
126                     }
127                 }
128                 nSendShortMessage(id, packedMsg, timeStamp);
129             } else {
130                 final byte[] data;
131                 if (message instanceof FastSysexMessage) {
132                     data = ((FastSysexMessage) message).getReadOnlyMessage();
133                 } else {
134                     data = message.getMessage();
135                 }
136                 final int dataLength = Math.min(length, data.length);
137                 if (dataLength > 0) {
138                     nSendLongMessage(id, data, dataLength, timeStamp);
139                 }
140             }
141         }
142 
143         /** shortcut for the Sun implementation */
sendPackedMidiMessage(int packedMsg, long timeStamp)144         synchronized void sendPackedMidiMessage(int packedMsg, long timeStamp) {
145             if (isOpen() && id != 0) {
146                 nSendShortMessage(id, packedMsg, timeStamp);
147             }
148         }
149     } // class MidiOutReceiver
150 
nOpen(int index)151     private native long nOpen(int index) throws MidiUnavailableException;
nClose(long id)152     private native void nClose(long id);
153 
nSendShortMessage(long id, int packedMsg, long timeStamp)154     private native void nSendShortMessage(long id, int packedMsg, long timeStamp);
nSendLongMessage(long id, byte[] data, int size, long timeStamp)155     private native void nSendLongMessage(long id, byte[] data, int size, long timeStamp);
nGetTimeStamp(long id)156     private native long nGetTimeStamp(long id);
157 
158 } // class MidiOutDevice
159