/**
* Metronome that counts samples rendered to make the click. --pjl (original code by PJS)
*/
package com.frinika.sequencer;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import uk.org.toot.audio.core.AudioBuffer;
import uk.org.toot.audio.core.AudioProcess;
import uk.org.toot.audio.server.AudioServer;
import com.frinika.project.ProjectContainer;
import com.frinika.synth.envelope.MidiVolume;
public class SampleBasedMetronome implements AudioProcess, SequencerListener {
// MyTracker tracker;
float[] sampleData;
float level = 0f;
boolean active = false;
ProjectContainer project;
int metSamplePos = 0;
long framePtr=0;
private int doClick=0;
public SampleBasedMetronome(ProjectContainer project) throws Exception {
// super(project.getAudioServer(), getFramePos(project.getSequencer(), project.getAudioServer(),
// 0));
// super(project.getAudioServer().openAudioOutput(project.getAudioServer().getAvailableOutputNames().get(0),null));
this.project = project;
final FrinikaSequencer sequencer=project.getSequencer();
final AudioServer audioServer=project.getAudioServer();
// this.tracker = tracker;
sequencer.addSequencerListener(this);
try {
AudioInputStream stream = AudioSystem
.getAudioInputStream(ClassLoader
.getSystemResource("sounds/metronome1.wav"));
sampleData = new float[(int) stream.getFrameLength()];
int index = 0;
byte[] frame = new byte[2];
int b = stream.read(frame);
while (b != -1) {
sampleData[index++] = (((frame[1] * 256) + (frame[0] & 0xff)) / 32768f);
b = stream.read(frame);
}
} catch (Exception e) {
e.printStackTrace();
}
// project.getMixer().getMainBus().setOutputProcess(this);
}
public void dispose() {
project.getSequencer().removeSequencerListener(this);
}
public void setVelocity(int velocity) {
if (velocity > 0) {
active = true;
level = MidiVolume.midiVolumeToAmplitudeRatio(velocity);
doClick=4;
} else {
active = false;
}
}
long nextClick() {
// double ticksPerBeat=project.getSequence().getResolution();
double bpSec=project.getSequencer().getTempoInBPM()/60.0; // uS/beat
double fs=project.getAudioServer().getSampleRate(); // /s
double samplesPerClick = fs/bpSec;
long nClick=((framePtr+(long)samplesPerClick-1)/(long)samplesPerClick);
return (long)(nClick*samplesPerClick);
}
public int processAudio(AudioBuffer buffer) {
if (!active) {
return AUDIO_OK;
}
if (!(doClick > 0 || project.getSequencer().isRunning())) return AUDIO_OK;
int size=buffer.getSampleCount();
int start=0;
long nextClick=nextClick();
// System. out.println(framePtr + " " + nextClick);
if (metSamplePos >= sampleData.length) {
doClick--;
if (framePtr+size < nextClick) {
framePtr+=size;
return AUDIO_OK;
}
start=(int) (nextClick-framePtr);
metSamplePos=0;
}
float left[] = buffer.getChannel(0);
float right[] = buffer.getChannel(1);
framePtr+=start;
for (int n = start; (n < size)
&& (metSamplePos < sampleData.length); n++,framePtr++) {
if (framePtr == nextClick) {
metSamplePos=0;
}
left[n] += sampleData[metSamplePos] * level;
right[n] += sampleData[metSamplePos++] * level;
}
return AUDIO_OK; // super.processAudio(buffer);
}
public void open() {
// TODO Auto-generated method stub
}
public void close() {
// TODO Auto-generated method stub
}
long getFramePos() {
FrinikaSequencer sequencer=project.getSequencer();
AudioServer audioServer=project.getAudioServer();
return (long) (((sequencer.getMicrosecondPosition()) * audioServer
.getSampleRate()) / 1000000);
}
public void beforeStart() {
framePtr = getFramePos();
}
public void start() {
// active=true;
}
public void stop() {
// active=false;
}
}