/*
* Created on Jul 19, 2006
*
* Copyright (c) 2006 P.J.Leonard
*
* 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
*/
package com.frinika.sequencer.model.audio;
import com.frinika.audio.io.AudioReader;
import com.frinika.audio.toot.SynchronizedAudioProcess;
import java.io.IOException;
import uk.org.toot.audio.core.AudioBuffer;
import uk.org.toot.audio.server.AudioServer;
import com.frinika.sequencer.FrinikaSequencer;
import com.frinika.sequencer.SongPositionListener;
public class AudioStreamVoice extends SynchronizedAudioProcess {
AudioReader ais;
boolean running = false;
byte[] byteBuffer = null;
int nChannel;
long clipStartPositionInMillis;
long clipStartPositionInFrames;
long framePos = -1; // current frame in file
private float sampleRate;
/**
* Construct a DAAudioStreamVoice. This is an extension of the
* SynchronizedVoice which uses a sequencer as synchronization source.
*
* The extended SynchronizedVoice class requires that we initially provide
* an offset for where in the clip to start (initialFramePos parameter), but
* the synchronization will then correct the position as the clip is
* playing. Thus we'll use the same formula as the synchronization to
* calculate the initialFramePos.
*
* @param voiceServer -
* The voice server we're playing in
* @param sequencer -
* the sequencer that we are playing in
* @param ais -
* The audio clip input stream
* @param clipStartTimePosition -
* The start time in microseconds relative to Start time relative
* to sequencer zero time
* @param modulator
* Evelope for the audio (can be null)
* @throws Exception
*/
public AudioStreamVoice(final AudioServer audioServer,
final FrinikaSequencer sequencer, final AudioReader ais,
final long clipStartTimePosition1)
throws Exception {
super(audioServer, 0);
//getFramePos(sequencer, audioServer,clipStartTimePosition1));
// try{
// throw new Exception(" FIXME ME" );
// }catch(Exception e){
// e.printStackTrace();
// }
this.sampleRate=audioServer.getSampleRate();
this.ais = ais;
setRealStartTime(clipStartTimePosition1);
nChannel = ais.getFormat().getChannels();
sequencer.addSongPositionListener(new SongPositionListener() {
public void notifyTickPosition(long tick) {
setRunning(sequencer.isRunning());
setFramePos(getFramePos(sequencer, audioServer,
clipStartPositionInMillis));
}
public boolean requiresNotificationOnEachTick() {
return false;
}
});
}
long milliToFrame(double t) {
return (long) ((t * sampleRate) / 1000000);
}
private static long getFramePos(FrinikaSequencer sequencer,
AudioServer audioServer, long clipStartTimePosition) {
return (long) (((sequencer.getMicrosecondPosition() - clipStartTimePosition) * audioServer
.getSampleRate()) / 1000000);
}
/**
* Tell the voice whether to play or not (if the sequencer is running)
*/
public void setRunning(final boolean running) {
AudioStreamVoice.this.running = running;
}
@Override
public void processAudioSynchronized(AudioBuffer buffer) {
if (!running)
return;
boolean realTime = buffer.isRealTime();
// Correct byte buffer size
if (byteBuffer == null
|| byteBuffer.length != buffer.getSampleCount() * 2 * nChannel)
byteBuffer = new byte[buffer.getSampleCount() * 2 * nChannel];
long seekPos = getFramePos();
if (seekPos != framePos) {
// S ystem.out.println(" Reposition file pointer ");
try {
ais.seekFrame(seekPos, realTime);
framePos = seekPos;
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
ais.processAudio(buffer);
if (ais.getChannels() == 1) {
buffer.copyChannel(0, 1);
}
framePos += buffer.getSampleCount();
}
public void setRealStartTime(long realStartTime) {
clipStartPositionInMillis = realStartTime;
clipStartPositionInFrames = (long) ((clipStartPositionInMillis * sampleRate) / 1000000);
}
public void close() {
}
public void open() {
}
}