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