2076 lines · 71.0 KB · Java
RawDownload
/* * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.media.sound; import java.io.ByteArrayOutputStream;import java.io.ByteArrayInputStream;import java.io.DataOutputStream;import java.io.IOException;import java.io.InputStream; import java.util.ArrayList;import java.util.List; import javax.sound.midi.*;  /** * A Real Time Sequencer * * @author Florian Bomers */ /* TODO: * - rename PlayThread to PlayEngine (because isn't a thread) */class RealTimeSequencer extends AbstractMidiDevice implements Sequencer, AutoConnectSequencer {     // STATIC VARIABLES     /** debugging flags */    private final static boolean DEBUG_PUMP = false;    private final static boolean DEBUG_PUMP_ALL = false;     /**     * Event Dispatcher thread. Should be using a shared event     * dispatcher instance with a factory in EventDispatcher     */    private static final EventDispatcher eventDispatcher;     /**     * All RealTimeSequencers share this info object.     */    static final RealTimeSequencerInfo info = new RealTimeSequencerInfo();      private static Sequencer.SyncMode[] masterSyncModes = { Sequencer.SyncMode.INTERNAL_CLOCK };    private static Sequencer.SyncMode[] slaveSyncModes  = { Sequencer.SyncMode.NO_SYNC };     private static Sequencer.SyncMode masterSyncMode    = Sequencer.SyncMode.INTERNAL_CLOCK;    private static Sequencer.SyncMode slaveSyncMode     = Sequencer.SyncMode.NO_SYNC;      /**     * Sequence on which this sequencer is operating.     */    private Sequence sequence = null;     // caches     /**     * Same for setTempoInMPQ...     * -1 means not set.     */    private double cacheTempoMPQ = -1;      /**     * cache value for tempo factor until sequence is set     * -1 means not set.     */    private float cacheTempoFactor = -1;      /** if a particular track is muted */    private boolean[] trackMuted = null;    /** if a particular track is solo */    private boolean[] trackSolo = null;     /** tempo cache for getMicrosecondPosition */    private MidiUtils.TempoCache tempoCache = new MidiUtils.TempoCache();     /**     * True if the sequence is running.     */    private boolean running = false;      /** the thread for pushing out the MIDI messages */    private PlayThread playThread;      /**     * True if we are recording     */    private boolean recording = false;      /**     * List of tracks to which we're recording     */    private List recordingTracks = new ArrayList();      private long loopStart = 0;    private long loopEnd = -1;    private int loopCount = 0;      /**     * Meta event listeners     */    private ArrayList metaEventListeners = new ArrayList();      /**     * Control change listeners     */    private ArrayList controllerEventListeners = new ArrayList();      /** automatic connection support */    private boolean autoConnect = false;     /** if we need to autoconnect at next open */    private boolean doAutoConnectAtNextOpen = false;     /** the receiver that this device is auto-connected to */    Receiver autoConnectedReceiver = null;      static {        // create and start the global event thread        eventDispatcher = new EventDispatcher();        eventDispatcher.start();    }      /* ****************************** CONSTRUCTOR ****************************** */     protected RealTimeSequencer() throws MidiUnavailableException {        super(info);         if (Printer.trace) Printer.trace(">> RealTimeSequencer CONSTRUCTOR");        if (Printer.trace) Printer.trace("<< RealTimeSequencer CONSTRUCTOR completed");    }      /* ****************************** SEQUENCER METHODS ******************** */     public synchronized void setSequence(Sequence sequence)        throws InvalidMidiDataException {         if (Printer.trace) Printer.trace(">> RealTimeSequencer: setSequence(" + sequence +")");         if (sequence != this.sequence) {            if (this.sequence != null && sequence == null) {                setCaches();                stop();                // initialize some non-cached values                trackMuted = null;                trackSolo = null;                loopStart = 0;                loopEnd = -1;                loopCount = 0;                if (getDataPump() != null) {                    getDataPump().setTickPos(0);                    getDataPump().resetLoopCount();                }            }             if (playThread != null) {                playThread.setSequence(sequence);            }             // store this sequence (do not copy - we want to give the possibility            // of modifying the sequence at runtime)            this.sequence = sequence;             if (sequence != null) {                tempoCache.refresh(sequence);                // rewind to the beginning                setTickPosition(0);                // propagate caches                propagateCaches();            }        }        else if (sequence != null) {            tempoCache.refresh(sequence);            if (playThread != null) {                playThread.setSequence(sequence);            }        }         if (Printer.trace) Printer.trace("<< RealTimeSequencer: setSequence(" + sequence +") completed");    }      public synchronized void setSequence(InputStream stream) throws IOException, InvalidMidiDataException {         if (Printer.trace) Printer.trace(">> RealTimeSequencer: setSequence(" + stream +")");         if (stream == null) {            setSequence((Sequence) null);            return;        }         Sequence seq = MidiSystem.getSequence(stream); // can throw IOException, InvalidMidiDataException         setSequence(seq);         if (Printer.trace) Printer.trace("<< RealTimeSequencer: setSequence(" + stream +") completed");     }      public Sequence getSequence() {        return sequence;    }      public synchronized void start() {        if (Printer.trace) Printer.trace(">> RealTimeSequencer: start()");         // sequencer not open: throw an exception        if (!isOpen()) {            throw new IllegalStateException("sequencer not open");        }         // sequence not available: throw an exception        if (sequence == null) {            throw new IllegalStateException("sequence not set");        }         // already running: return quietly        if (running == true) {            return;        }         // start playback        implStart();         if (Printer.trace) Printer.trace("<< RealTimeSequencer: start() completed");    }      public synchronized void stop() {        if (Printer.trace) Printer.trace(">> RealTimeSequencer: stop()");         if (!isOpen()) {            throw new IllegalStateException("sequencer not open");        }        stopRecording();         // not running; just return        if (running == false) {            if (Printer.trace) Printer.trace("<< RealTimeSequencer: stop() not running!");            return;        }         // stop playback        implStop();         if (Printer.trace) Printer.trace("<< RealTimeSequencer: stop() completed");    }      public boolean isRunning() {        return running;    }      public void startRecording() {        if (!isOpen()) {            throw new IllegalStateException("Sequencer not open");        }         start();        recording = true;    }      public void stopRecording() {        if (!isOpen()) {            throw new IllegalStateException("Sequencer not open");        }        recording = false;    }      public boolean isRecording() {        return recording;    }      public void recordEnable(Track track, int channel) {        if (!findTrack(track)) {            throw new IllegalArgumentException("Track does not exist in the current sequence");        }         synchronized(recordingTracks) {            RecordingTrack rc = RecordingTrack.get(recordingTracks, track);            if (rc != null) {                rc.channel = channel;            } else {                recordingTracks.add(new RecordingTrack(track, channel));            }        }     }      public void recordDisable(Track track) {        synchronized(recordingTracks) {            RecordingTrack rc = RecordingTrack.get(recordingTracks, track);            if (rc != null) {                recordingTracks.remove(rc);            }        }     }      private boolean findTrack(Track track) {        boolean found = false;        if (sequence != null) {            Track[] tracks = sequence.getTracks();            for (int i = 0; i < tracks.length; i++) {                if (track == tracks[i]) {                    found = true;                    break;                }            }        }        return found;    }      public float getTempoInBPM() {        if (Printer.trace) Printer.trace(">> RealTimeSequencer: getTempoInBPM() ");         return (float) MidiUtils.convertTempo(getTempoInMPQ());    }      public void setTempoInBPM(float bpm) {        if (Printer.trace) Printer.trace(">> RealTimeSequencer: setTempoInBPM() ");        if (bpm <= 0) {            // should throw IllegalArgumentException            bpm = 1.0f;        }         setTempoInMPQ((float) MidiUtils.convertTempo((double) bpm));    }      public float getTempoInMPQ() {        if (Printer.trace) Printer.trace(">> RealTimeSequencer: getTempoInMPQ() ");         if (needCaching()) {            // if the sequencer is closed, return cached value            if (cacheTempoMPQ != -1) {                return (float) cacheTempoMPQ;            }            // if sequence is set, return current tempo            if (sequence != null) {                return tempoCache.getTempoMPQAt(getTickPosition());            }             // last resort: return a standard tempo: 120bpm            return (float) MidiUtils.DEFAULT_TEMPO_MPQ;        }        return (float)getDataPump().getTempoMPQ();    }      public void setTempoInMPQ(float mpq) {        if (mpq <= 0) {            // should throw IllegalArgumentException            mpq = 1.0f;        }         if (Printer.trace) Printer.trace(">> RealTimeSequencer: setTempoInMPQ() ");         if (needCaching()) {            // cache the value            cacheTempoMPQ = mpq;        } else {            // set the native tempo in MPQ            getDataPump().setTempoMPQ(mpq);             // reset the tempoInBPM and tempoInMPQ values so we won't use them again            cacheTempoMPQ = -1;        }    }      public void setTempoFactor(float factor) {        if (factor <= 0) {            // should throw IllegalArgumentException            return;        }         if (Printer.trace) Printer.trace(">> RealTimeSequencer: setTempoFactor() ");         if (needCaching()) {            cacheTempoFactor = factor;        } else {            getDataPump().setTempoFactor(factor);            // don't need cache anymore            cacheTempoFactor = -1;        }    }      public float getTempoFactor() {        if (Printer.trace) Printer.trace(">> RealTimeSequencer: getTempoFactor() ");         if (needCaching()) {            if (cacheTempoFactor != -1) {                return cacheTempoFactor;            }            return 1.0f;        }        return getDataPump().getTempoFactor();    }      public long getTickLength() {        if (Printer.trace) Printer.trace(">> RealTimeSequencer: getTickLength() ");         if (sequence == null) {            return 0;        }         return sequence.getTickLength();    }      public synchronized long getTickPosition() {        if (Printer.trace) Printer.trace(">> RealTimeSequencer: getTickPosition() ");         if (getDataPump() == null || sequence == null) {            return 0;        }         return getDataPump().getTickPos();    }      public synchronized void setTickPosition(long tick) {        if (tick < 0) {            // should throw IllegalArgumentException            return;        }         if (Printer.trace) Printer.trace(">> RealTimeSequencer: setTickPosition("+tick+") ");         if (getDataPump() == null) {            if (tick != 0) {                // throw new InvalidStateException("cannot set position in closed state");            }        }        else if (sequence == null) {            if (tick != 0) {                // throw new InvalidStateException("cannot set position if sequence is not set");            }        } else {            getDataPump().setTickPos(tick);        }    }      public long getMicrosecondLength() {        if (Printer.trace) Printer.trace(">> RealTimeSequencer: getMicrosecondLength() ");         if (sequence == null) {            return 0;

Lines 1–500 of 2076.