📄 tsequencer.java
字号:
/* * TSequencer.java *//* * Copyright (c) 1999 - 2003 by Matthias Pfisterer * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as published * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */package org.tritonus.share.midi;import java.io.IOException;import java.io.InputStream;import java.util.BitSet;import java.util.Collection;import java.util.Iterator;import java.util.Set;import javax.sound.midi.ControllerEventListener;import javax.sound.midi.InvalidMidiDataException;import javax.sound.midi.MetaEventListener;import javax.sound.midi.MetaMessage;import javax.sound.midi.MidiDevice;import javax.sound.midi.MidiMessage;import javax.sound.midi.MidiSystem;import javax.sound.midi.Sequence;import javax.sound.midi.Sequencer;import javax.sound.midi.ShortMessage;import org.tritonus.share.ArraySet;import org.tritonus.share.TDebug;public abstract class TSequencer extends TMidiDevice implements Sequencer{ private static final float MPQ_BPM_FACTOR = 6.0E7F; // This is for use in Collection.toArray(Object[]). private static final SyncMode[] EMPTY_SYNCMODE_ARRAY = new SyncMode[0]; private boolean m_bRunning; /** The Sequence to play or to record to. */ private Sequence m_sequence; /** The listeners that want to be notified of MetaMessages. */ private Set m_metaListeners; /** The listeners that want to be notified of control change events. * They are organized as follows: this array is indexed with * the number of the controller change events listeners are * interested in. If there is any interest, the array element * contains a reference to a Set containing the listeners. * These sets are allocated on demand. */ private Set[] m_aControllerListeners; private float m_fNominalTempoInMPQ; private float m_fTempoFactor; private Collection m_masterSyncModes; private Collection m_slaveSyncModes; private SyncMode m_masterSyncMode; private SyncMode m_slaveSyncMode; private BitSet m_muteBitSet; private BitSet m_soloBitSet; /** Contains the enabled state of the tracks. This BitSet holds the pre-calculated effect of mute and solo status. */ private BitSet m_enabledBitSet; protected TSequencer(MidiDevice.Info info, Collection masterSyncModes, Collection slaveSyncModes) { super(info); if (TDebug.TraceSequencer) { TDebug.out("TSequencer.<init>(): begin"); } m_bRunning = false; m_sequence = null; m_metaListeners = new ArraySet(); m_aControllerListeners = new Set[128]; setTempoInMPQ(500000); setTempoFactor(1.0F); m_masterSyncModes = masterSyncModes; m_slaveSyncModes = slaveSyncModes; if (getMasterSyncModes().length > 0) { m_masterSyncMode = getMasterSyncModes()[0]; } if (getSlaveSyncModes().length > 0) { m_slaveSyncMode = getSlaveSyncModes()[0]; } m_muteBitSet = new BitSet(); m_soloBitSet = new BitSet(); m_enabledBitSet = new BitSet(); updateEnabled(); if (TDebug.TraceSequencer) { TDebug.out("TSequencer.<init>(): end"); } } public void setSequence(Sequence sequence) throws InvalidMidiDataException { if (TDebug.TraceSequencer) { TDebug.out("TSequencer.setSequence(Sequence): begin"); } // TODO: what if playing is in progress? m_sequence = sequence; // yes, this is required by the specification setTempoFactor(1.0F); if (TDebug.TraceSequencer) { TDebug.out("TSequencer.setSequence(Sequence): end"); } } public void setSequence(InputStream inputStream) throws InvalidMidiDataException, IOException { if (TDebug.TraceSequencer) { TDebug.out("TSequencer.setSequence(InputStream): begin"); } Sequence sequence = MidiSystem.getSequence(inputStream); setSequence(sequence); if (TDebug.TraceSequencer) { TDebug.out("TSequencer.setSequence(InputStream): end"); } } public Sequence getSequence() { if (TDebug.TraceSequencer) { TDebug.out("TSequencer.getSequence(): begin"); } if (TDebug.TraceSequencer) { TDebug.out("TSequencer.getSequence(): end"); } return m_sequence; } public synchronized void start() { if (TDebug.TraceSequencer) { TDebug.out("TSequencer.start(): begin"); } checkOpen(); if (! isRunning()) { m_bRunning = true; // TODO: perhaps check if sequence present startImpl(); } if (TDebug.TraceSequencer) { TDebug.out("TSequencer.start(): end"); } } /** * Subclasses have to override this method to be notified of * starting. */ protected void startImpl() { if (TDebug.TraceMidiDevice) { TDebug.out("TSequencer.startImpl(): begin"); } if (TDebug.TraceMidiDevice) { TDebug.out("TSequencer.startImpl(): end"); } } public synchronized void stop() { if (TDebug.TraceSequencer) { TDebug.out("TSequencer.stop(): begin"); } checkOpen(); if (isRunning()) { stopImpl(); m_bRunning = false; } if (TDebug.TraceSequencer) { TDebug.out("TSequencer.stop(): end"); } } /** * Subclasses have to override this method to be notified of * stopping. */ protected void stopImpl() { if (TDebug.TraceMidiDevice) { TDebug.out("TSequencer.stopImpl(): begin"); } if (TDebug.TraceMidiDevice) { TDebug.out("TSequencer.stopImpl(): end"); } } public synchronized boolean isRunning() { return m_bRunning; } /** Checks if the Sequencer is open. This method is intended to be called by {@link javax.sound.midi.Sequencer#start start}, {@link javax.sound.midi.Sequencer#stop stop}, {@link javax.sound.midi.Sequencer#startRecording startRecording} and {@link javax.sound.midi.Sequencer#stop stopRecording}. @throws IllegalStateException if the <code>Sequencer</code> is not open */ protected void checkOpen() { if (! isOpen()) { throw new IllegalStateException("Sequencer is not open"); } } /** Returns the resolution (ticks per quarter) of the current sequence. If no sequence is set, a bogus default value != 0 is returned. */ protected int getResolution() { if (TDebug.TraceSequencer) { TDebug.out("TSequencer.getResolution(): begin"); } Sequence sequence = getSequence(); int nResolution; if (sequence != null) { nResolution = sequence.getResolution(); } else { nResolution = 1; } if (TDebug.TraceSequencer) { TDebug.out("TSequencer.getResolution(): end"); } return nResolution; } protected void setRealTempo() { if (TDebug.TraceSequencer) { TDebug.out("TSequencer.setRealTempo(): begin"); } float fRealTempo = getTempoInMPQ() / getTempoFactor(); if (TDebug.TraceSequencer) { TDebug.out("TSequencer.setRealTempo(): real tempo: " + fRealTempo); } setTempoImpl(fRealTempo); if (TDebug.TraceSequencer) { TDebug.out("TSequencer.setRealTempo(): end"); } } public float getTempoInBPM() { if (TDebug.TraceSequencer) { TDebug.out("TSequencer.getTempoInBPM(): begin"); } float fBPM = MPQ_BPM_FACTOR / getTempoInMPQ(); if (TDebug.TraceSequencer) { TDebug.out("TSequencer.getTempoInBPM(): end"); } return fBPM; } public void setTempoInBPM(float fBPM) { if (TDebug.TraceSequencer) { TDebug.out("TSequencer.setTempoInBPM(): begin"); } float fMPQ = MPQ_BPM_FACTOR / fBPM; setTempoInMPQ(fMPQ); if (TDebug.TraceSequencer) { TDebug.out("TSequencer.setTempoInBPM(): end"); } } public float getTempoInMPQ() { if (TDebug.TraceSequencer) { TDebug.out("TSequencer.getTempoInMPQ(): begin"); } if (TDebug.TraceSequencer) { TDebug.out("TSequencer.getTempoInMPQ(): end"); } return m_fNominalTempoInMPQ; } /** Sets the tempo. Implementation classes are required to call this method for changing the tempo in reaction to a tempo change event. */ public void setTempoInMPQ(float fMPQ) { if (TDebug.TraceSequencer) { TDebug.out("TSequencer.setTempoInMPQ(): begin"); } m_fNominalTempoInMPQ = fMPQ; setRealTempo(); if (TDebug.TraceSequencer) { TDebug.out("TSequencer.setTempoInMPQ(): end"); } } public void setTempoFactor(float fFactor) { if (TDebug.TraceSequencer) { TDebug.out("TSequencer.setTempoFactor(): begin"); } m_fTempoFactor = fFactor; setRealTempo(); if (TDebug.TraceSequencer) { TDebug.out("TSequencer.setTempoFactor(): end"); } } public float getTempoFactor() { if (TDebug.TraceSequencer) { TDebug.out("TSequencer.getTempoFactor(): begin"); } if (TDebug.TraceSequencer) { TDebug.out("TSequencer.getTempoFactor(): end"); } return m_fTempoFactor; } /** Change the tempo of the native sequencer part. * This method has to be defined by subclasses according * to the native facilities they use for sequenceing. * The implementation should not take into account the * tempo factor. This is handled elsewhere. */ protected abstract void setTempoImpl(float fMPQ); // NOTE: has to be redefined if recording is done natively public long getTickLength() { if (TDebug.TraceSequencer) { TDebug.out("TSequencer.getTickLength(): begin"); } long lLength = 0; if (getSequence() != null) { lLength = getSequence().getTickLength(); } if (TDebug.TraceSequencer) { TDebug.out("TSequencer.getTickLength(): end"); } return lLength; } // NOTE: has to be redefined if recording is done natively public long getMicrosecondLength() { if (TDebug.TraceSequencer) { TDebug.out("TSequencer.getMicrosecondLength(): begin"); } long lLength = 0; if (getSequence() != null) { lLength = getSequence().getMicrosecondLength(); } if (TDebug.TraceSequencer) { TDebug.out("TSequencer.getMicrosecondLength(): end"); } return lLength; } public boolean addMetaEventListener(MetaEventListener listener) { synchronized (m_metaListeners) { return m_metaListeners.add(listener); } } public void removeMetaEventListener(MetaEventListener listener) { synchronized (m_metaListeners) { m_metaListeners.remove(listener); } } protected Iterator getMetaEventListeners() { synchronized (m_metaListeners) { return m_metaListeners.iterator(); } } protected void sendMetaMessage(MetaMessage message) { Iterator iterator = getMetaEventListeners(); while (iterator.hasNext()) { MetaEventListener metaEventListener = (MetaEventListener) iterator.next(); MetaMessage copiedMessage = (MetaMessage) message.clone(); metaEventListener.meta(copiedMessage); } } public int[] addControllerEventListener(ControllerEventListener listener, int[] anControllers) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -