package com.frinika.audio.toot; /* * Created on Feb 06, 2007 * * Copyright (c) 2004-2006 Peter Johan Salomonsen (http://www.petersalomonsen.com) * * http://www.frinika.com * * This file is part of Frinika. * * Frinika is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frinika 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 for more details. * * You should have received a copy of the GNU General Public License * along with Frinika; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ import uk.org.toot.audio.core.AudioBuffer; import uk.org.toot.audio.core.AudioProcess; import uk.org.toot.audio.server.AudioServer; /** * An audioprocess that can be synchronized from an outside timing source. Useful for e.g. audio playback to be synchronized * with a sequencer. * * @author Peter Johan Salomonsen */ public abstract class SynchronizedAudioProcess implements AudioProcess { long framePos = 0; /** * In case of a glitch - how many frames did we miss? */ int missedFrames = 0; /** * Number of milliseconds of glitch before synchronization if enforced * Windows recommendation: 50 ms, Linux 10-20 ms * TODO: Make this configurable from audio devices GUI */ int missedFramesToleranceMillis = 50; // Default 50 msecs AudioServer audioServer; /** * Construct a new Synchronized audioProcess. Note that in order for the initialFramePos to be valid, this voice * should be added to the voiceServer not too long after it's constructed * * Since glitches may "come and go" due to the missing accuracy in Java timing functions, synchronization is not * applied until a tolerance threshold is exceeded. * * @param voiceServer * @param initialFramePos - the initial position of playback start - relative to audio clip start */ public SynchronizedAudioProcess(AudioServer audioServer,long initialFramePos) { this.audioServer = audioServer; this.framePos = initialFramePos; } /** * Number of milliseconds of glitch before synchronization if enforced */ public int getMissedFramesToleranceMillis() { return missedFramesToleranceMillis; } /** * Set number of milliseconds of glitch before synchronization if enforced * @param missedFramesToleranceMillis */ public void setMissedFramesToleranceMillis(int missedFramesToleranceMillis) { this.missedFramesToleranceMillis = missedFramesToleranceMillis; } /** * The external timing source should regularly notify with the current frame position here * @param framePos */ public void setFramePos(final long framePos) { // For a samplerate of 44100 the glitch must be at least 45 frames for a 1 msec glitch int glitchMS = (int) (((framePos - SynchronizedAudioProcess.this.framePos) * 1000) / audioServer.getSampleRate()); // Only enfore synchronization if the glitch is beyond the tolerance if(Math.abs(glitchMS)>missedFramesToleranceMillis) { missedFrames = (int) (framePos - SynchronizedAudioProcess.this.framePos); SynchronizedAudioProcess.this.framePos = framePos; } } /** * Call this method from fillBufferSynchronized to get the framePos according to the external timing source * @return the current frame postion. */ protected final long getFramePos() { return framePos; } /** * Call this method from fillBufferSynchronized to get the number of missing frames (glitch) after an external sync notification * NOTE: Your tolerance on missed frames should not be too low - since timing functions like System.currentTimeMillis might slide * up to 50 ms on some systems. Your number of missed frames tolerance should be thereafter before correcting your * framepos. * * @return */ protected final int getMissedFrames() { return missedFrames; } public int processAudio(AudioBuffer buffer) { processAudioSynchronized(buffer); framePos += buffer.getSampleCount(); // Update framePos according to number of requested samples to fillBuffer missedFrames = 0; // Reset missedframes until next notification return AUDIO_OK; } public abstract void processAudioSynchronized(AudioBuffer buffer); }