📄 alsasequencer.java
字号:
/* * AlsaSequencer.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.midi.device.alsa;import java.util.Arrays;import javax.sound.midi.InvalidMidiDataException;import javax.sound.midi.MetaMessage;import javax.sound.midi.MidiDevice;import javax.sound.midi.MidiEvent;import javax.sound.midi.MidiMessage;import javax.sound.midi.MidiUnavailableException;import javax.sound.midi.Receiver;import javax.sound.midi.ShortMessage;import javax.sound.midi.Sequence;import javax.sound.midi.Sequencer;import javax.sound.midi.Track;import javax.sound.midi.Transmitter;import org.tritonus.lowlevel.alsa.AlsaSeq;import org.tritonus.lowlevel.alsa.AlsaSeqPortSubscribe;import org.tritonus.lowlevel.alsa.AlsaSeqQueueInfo;import org.tritonus.lowlevel.alsa.AlsaSeqEvent;import org.tritonus.lowlevel.alsa.AlsaSeqQueueStatus;import org.tritonus.lowlevel.alsa.AlsaSeqQueueTempo;import org.tritonus.share.TDebug;import org.tritonus.share.midi.MidiUtils;import org.tritonus.share.midi.TMidiDevice.TReceiver;import org.tritonus.share.midi.TMidiDevice.TTransmitter;import org.tritonus.share.midi.TSequencer;public class AlsaSequencer// TODO: derive from TPreloadingSequencerextends TSequencer{ /** The syncronization modes the sequencer can sync to. */ private static final SyncMode[] MASTER_SYNC_MODES = {SyncMode.INTERNAL_CLOCK}; /** The syncronization modes the sequencer can send. */ private static final SyncMode[] SLAVE_SYNC_MODES = {SyncMode.NO_SYNC, SyncMode.MIDI_SYNC}; /** The ALSA event tag used for MIDI clock events */ private static final int CLOCK_EVENT_TAG = 255; private AlsaSeq m_playbackAlsaSeq; private AlsaSeq m_recordingAlsaSeq; private int m_nRecordingPort; private int m_nPlaybackPort; private int m_nQueue; private AlsaSeqQueueInfo m_queueInfo; private AlsaSeqQueueStatus m_queueStatus; private AlsaSeqQueueTempo m_queueTempo; private AlsaMidiIn m_playbackAlsaMidiIn; private AlsaMidiOut m_playbackAlsaMidiOut; private AlsaMidiIn m_recordingAlsaMidiIn; private Thread m_loaderThread; private Thread m_syncThread; private AlsaSeqEvent m_queueControlEvent; private AlsaSeqEvent m_clockEvent; private boolean m_bRecording; private Track m_track; private AlsaSeqEvent m_allNotesOffEvent; private Sequencer.SyncMode m_oldSlaveSyncMode; private float m_fCachedRealMPQ; public AlsaSequencer(MidiDevice.Info info) { super(info, Arrays.asList(MASTER_SYNC_MODES), Arrays.asList(SLAVE_SYNC_MODES)); if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.<init>(): begin"); } m_fCachedRealMPQ = -1.0F; if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.<init>(): end"); } } private int getPlaybackClient() { int nClient = getPlaybackAlsaSeq().getClientId(); return nClient; } private int getPlaybackPort() { return m_nPlaybackPort; } private int getRecordingClient() { int nClient = getRecordingAlsaSeq().getClientId(); return nClient; } private int getRecordingPort() { return m_nRecordingPort; } private int getQueue() { return m_nQueue; } private AlsaSeqQueueStatus getQueueStatus() { return m_queueStatus; } private AlsaSeqQueueTempo getQueueTempo() { return m_queueTempo; } private AlsaSeq getPlaybackAlsaSeq() { return m_playbackAlsaSeq; } private AlsaSeq getRecordingAlsaSeq() { return m_recordingAlsaSeq; } private void updateQueueStatus() { if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.updateQueueStatus(): begin"); } // TODO: error handling // getRecordingAlsaSeq().getQueueStatus(getQueue(), getQueueStatus()); getPlaybackAlsaSeq().getQueueStatus(getQueue(), getQueueStatus()); if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.updateQueueStatus(): end"); } } protected void openImpl() { if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.openImpl(): begin"); } m_recordingAlsaSeq = new AlsaSeq("Tritonus ALSA Sequencer (recording/synchronization)"); m_nRecordingPort = getRecordingAlsaSeq().createPort("recording/synchronization port", AlsaSeq.SND_SEQ_PORT_CAP_WRITE | AlsaSeq.SND_SEQ_PORT_CAP_SUBS_WRITE | AlsaSeq.SND_SEQ_PORT_CAP_READ | AlsaSeq.SND_SEQ_PORT_CAP_SUBS_READ, 0, AlsaSeq.SND_SEQ_PORT_TYPE_APPLICATION, 0, 0, 0); m_playbackAlsaSeq = new AlsaSeq("Tritonus ALSA Sequencer (playback)"); m_nPlaybackPort = getPlaybackAlsaSeq().createPort("playback port", AlsaSeq.SND_SEQ_PORT_CAP_WRITE | AlsaSeq.SND_SEQ_PORT_CAP_SUBS_WRITE | AlsaSeq.SND_SEQ_PORT_CAP_READ | AlsaSeq.SND_SEQ_PORT_CAP_SUBS_READ, 0, AlsaSeq.SND_SEQ_PORT_TYPE_APPLICATION, 0, 0, 0); m_nQueue = getPlaybackAlsaSeq().allocQueue(); m_queueInfo = new AlsaSeqQueueInfo(); m_queueStatus = new AlsaSeqQueueStatus(); m_queueTempo = new AlsaSeqQueueTempo(); getPlaybackAlsaSeq().getQueueInfo(getQueue(), m_queueInfo); m_queueInfo.setLocked(false); getPlaybackAlsaSeq().setQueueInfo(getQueue(), m_queueInfo); m_playbackAlsaMidiOut = new AlsaMidiOut(getPlaybackAlsaSeq(), getPlaybackPort(), getQueue()); m_playbackAlsaMidiOut.setHandleMetaMessages(true); getRecordingAlsaSeq().setQueueUsage(getQueue(), true); // this establishes the subscription, too AlsaMidiIn.AlsaMidiInListener playbackListener = new PlaybackAlsaMidiInListener(); m_playbackAlsaMidiIn = new AlsaMidiIn(getPlaybackAlsaSeq(), getPlaybackPort(), getPlaybackClient(), getPlaybackPort(), playbackListener); // start the receiving thread m_playbackAlsaMidiIn.start(); if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.openImpl(): end"); } m_queueControlEvent = new AlsaSeqEvent(); m_clockEvent = new AlsaSeqEvent(); m_clockEvent.setCommon( AlsaSeq.SND_SEQ_EVENT_CLOCK, // type AlsaSeq.SND_SEQ_TIME_STAMP_TICK | AlsaSeq.SND_SEQ_TIME_MODE_ABS, CLOCK_EVENT_TAG, // tag getQueue(), 0L, // timestamp; not yet known 0, // source client getRecordingPort(), // source port AlsaSeq.SND_SEQ_ADDRESS_SUBSCRIBERS, // dest client AlsaSeq.SND_SEQ_ADDRESS_UNKNOWN); // dest port m_allNotesOffEvent = new AlsaSeqEvent(); m_oldSlaveSyncMode = getSlaveSyncMode(); if (m_fCachedRealMPQ != -1.0F) { setTempoImpl(m_fCachedRealMPQ); m_fCachedRealMPQ = -1.0F; } m_loaderThread = new LoaderThread(); m_loaderThread.start(); // this is for sending clock events // m_syncThread = new MasterSynchronizer(); // m_syncThread.start(); } protected void closeImpl() { if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.closeImpl(): begin"); } m_playbackAlsaMidiIn.interrupt(); m_playbackAlsaMidiIn = null; getQueueStatus().free(); m_queueStatus = null; getQueueTempo().free(); m_queueTempo = null; // TODO: // m_aSequencer.releaseQueue(getQueue()); // m_aSequencer.destroyPort(getPort()); getRecordingAlsaSeq().close(); m_recordingAlsaSeq = null; getPlaybackAlsaSeq().close(); m_playbackAlsaSeq = null; m_queueControlEvent.free(); m_queueControlEvent = null; m_clockEvent.free(); m_clockEvent = null; m_allNotesOffEvent.free(); m_allNotesOffEvent = null; if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.closeImpl(): end"); } } protected void startImpl() { if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.startImpl(): begin"); } if (getTickPosition() == 0) { startQueue(); } else { continueQueue(); } synchronized (m_loaderThread) { if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.startImpl(): notifying loader thread"); } m_loaderThread.notify(); } // TODO: should depend on sync mode// synchronized (m_syncThread)// {// if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.startImpl(): notifying synchronizer thread"); }// m_syncThread.notify();// } if (! getSlaveSyncMode(). equals(Sequencer.SyncMode.NO_SYNC)) { sendStartEvent(); } if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.startImpl(): end"); } } protected void stopImpl() { if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.stopImpl(): begin"); } stopQueue(); sendAllNotesOff(); // should be in base class? stopRecording(); if (! getSlaveSyncMode(). equals(Sequencer.SyncMode.NO_SYNC)) { sendStopEvent(); } if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.stopImpl(): end"); } } public boolean isRunning() { if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.isRunning(): begin"); } boolean bRunning = false; if (isOpen()) { updateQueueStatus(); int nStatus = getQueueStatus().getStatus(); if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.isRunning(): queue status: " + nStatus); } bRunning = (nStatus != 0); } if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.isRunning(): end"); } return bRunning; } public void startRecording() { checkOpen(); // may throw IllegalStateException m_bRecording = true; start(); } public void stopRecording() { checkOpen(); // may throw IllegalStateException m_bRecording = false; } public boolean isRecording() { return m_bRecording; } // name should be: enableRecording public void recordEnable(Track track, int nChannel) { // TODO: hacky m_track = track; } // name should be: disableRecording public void recordDisable(Track track) { // TODO: } protected void setTempoImpl(float fRealMPQ) { if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.setTempoImpl(): begin"); } if (isOpen()) { if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.setTempoImpl(): setting tempo to " + (int) fRealMPQ); } getQueueTempo().setTempo((int) fRealMPQ); getQueueTempo().setPpq(getResolution()); getPlaybackAlsaSeq().setQueueTempo(getQueue(), getQueueTempo()); } else { if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.setTempoImpl(): ignoring because sequencer is not opened"); } m_fCachedRealMPQ = fRealMPQ; } if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.setTempoImpl(): end"); } } public long getTickPosition() { if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.getTickPosition(): begin"); } long lPosition; if (isOpen()) { updateQueueStatus(); lPosition = getQueueStatus().getTickTime(); } else { if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.getTickPosition(): sequencer not open, returning 0"); } lPosition = 0; } if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.getTickPosition(): end"); } return lPosition; } public void setTickPosition(long lTick) { if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.setTickPosition(): begin"); } if (isOpen()) { int nSourcePort = getRecordingPort(); int nQueue = getQueue(); long lTime = lTick; sendQueueControlEvent( AlsaSeq.SND_SEQ_EVENT_SETPOS_TICK, AlsaSeq.SND_SEQ_TIME_STAMP_REAL | AlsaSeq.SND_SEQ_TIME_MODE_REL, 0, AlsaSeq.SND_SEQ_QUEUE_DIRECT, 0L, nSourcePort, AlsaSeq.SND_SEQ_CLIENT_SYSTEM, AlsaSeq.SND_SEQ_PORT_SYSTEM_TIMER, nQueue, 0, lTime); } else { if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.setTickPosition(): ignored because sequencer is not open"); } } if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.setTickPosition(): end"); } } public long getMicrosecondPosition() { if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.getMicrosecondPosition(): begin"); } long lPosition; if (isOpen()) { updateQueueStatus(); long lNanoSeconds = getQueueStatus().getRealTime(); lPosition = lNanoSeconds / 1000; } else { if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.getMicrosecondPosition(): sequencer not open, returning 0"); } lPosition = 0; } if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.getMicrosecondPosition(): end"); } return lPosition; } public void setMicrosecondPosition(long lMicroseconds) { if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.setMicrosecondPosition(): begin"); } if (isOpen()) { long lNanoSeconds = lMicroseconds * 1000; int nSourcePort = getRecordingPort(); int nQueue = getQueue(); long lTime = lNanoSeconds; sendQueueControlEvent( AlsaSeq.SND_SEQ_EVENT_SETPOS_TIME, AlsaSeq.SND_SEQ_TIME_STAMP_REAL | AlsaSeq.SND_SEQ_TIME_MODE_REL, 0, AlsaSeq.SND_SEQ_QUEUE_DIRECT, 0L, nSourcePort, AlsaSeq.SND_SEQ_CLIENT_SYSTEM, AlsaSeq.SND_SEQ_PORT_SYSTEM_TIMER, nQueue, 0, lTime); } else { if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.setMicrosecondPosition(): ignoring because sequencer is not open"); } } if (TDebug.TraceSequencer) { TDebug.out("AlsaSequencer.setMicrosecondPosition(): end"); } } protected void setMasterSyncModeImpl(SyncMode syncMode) { // TODO: } protected void setSlaveSyncModeImpl(SyncMode syncMode) { if (isRunning()) { if (m_oldSlaveSyncMode.equals(Sequencer.SyncMode.NO_SYNC) && (syncMode.equals(Sequencer.SyncMode.MIDI_SYNC) || syncMode.equals(Sequencer.SyncMode.MIDI_TIME_CODE)) ) { sendStartEvent(); // TODO: notify sync thread } else if ((m_oldSlaveSyncMode.equals(Sequencer.SyncMode.MIDI_SYNC) || m_oldSlaveSyncMode.equals(Sequencer.SyncMode.MIDI_TIME_CODE)) && syncMode.equals(Sequencer.SyncMode.NO_SYNC) ) { sendStopEvent(); // TODO: remove enqueued messages from queue (and buffer). perhaps do this by putting the code to do so after the main loop of the sync thread. } } } protected void setTrackEnabledImpl(int nTrack, boolean bEnabled) { /** If the enabled state changes to true, the events between the current playback position and the current loading position are enqueued. If the enabled state of a track changed to false, the events belonging to this track are removed from the queue, besides the 'off'-events. */ if (bEnabled) { // TODO: reload events } else { // TODO: remove events
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -