Skip to content

Commit

Permalink
Added support for custom Java-based editor.
Browse files Browse the repository at this point in the history
A custom Java-based editor can now be added to a JVstHost object. The view can modify parameter and program settings of the vst, as well as send midi messages to it. JVstHost has been modified such that all public methods are synchronized. This helps coordinate automation and ui threads which may try to access the plugin simultaneously.
  • Loading branch information
mhroth committed Oct 31, 2008
1 parent 1534c2c commit 10f57d4
Show file tree
Hide file tree
Showing 14 changed files with 506 additions and 437 deletions.
Binary file modified JVstHost.jar
Binary file not shown.
Binary file added res/black_key.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added res/white_key.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Expand Up @@ -19,41 +19,40 @@
* *
*/ */


package com.synthbot.minihost; package com.synthbot.audioio.vst;


import com.synthbot.audioplugin.vst.JVstHost; import com.synthbot.audioplugin.vst.JVstHost;
import java.util.Vector;
import javax.sound.midi.ShortMessage;
import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem; import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine; import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine; import javax.sound.sampled.SourceDataLine;


public class AudioThread implements Runnable { /**
* JVstAudioThread implements a continuously running audio stream, calling
* processReplacing on a single vst and sending the result to the sound output.
*/
public class JVstAudioThread implements Runnable {


private JVstHost vst; private JVstHost vst;
private volatile boolean keepRunning;
private float[][] fOutputs; private float[][] fOutputs;
private byte[] bOutput; private byte[] bOutput;
private int blockSize; private int blockSize;
private int numOutputs; private int numOutputs;
private AudioFormat audioFormat; private AudioFormat audioFormat;
private SourceDataLine sourceDataLine; private SourceDataLine sourceDataLine;


private Vector<ShortMessage> pendingMidi;

private static final float ShortMaxValueAsFloat = (float) Short.MAX_VALUE; private static final float ShortMaxValueAsFloat = (float) Short.MAX_VALUE;


public AudioThread(JVstHost vst) { public JVstAudioThread(JVstHost vst) {
addJVstHost(vst); this.vst = vst;
numOutputs = vst.numOutputs(); numOutputs = vst.numOutputs();
blockSize = vst.getBlockSize(); blockSize = vst.getBlockSize();
pendingMidi = new Vector<ShortMessage>(); fOutputs = new float[numOutputs][blockSize];
bOutput = new byte[numOutputs * blockSize * 2];


audioFormat = new AudioFormat((int) vst.getSampleRate(), 16, vst.numOutputs(), true, false); audioFormat = new AudioFormat((int) vst.getSampleRate(), 16, vst.numOutputs(), true, false);
DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, audioFormat); DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, audioFormat);
System.out.println("AudioThread::Sound card data line info:" + dataLineInfo.toString());


sourceDataLine = null; sourceDataLine = null;
try { try {
Expand All @@ -65,9 +64,17 @@ public AudioThread(JVstHost vst) {
System.exit(1); System.exit(1);
} }
} }

@Override
protected void finalize() {
// close the sourceDataLine properly when this object is garbage collected
sourceDataLine.drain();
sourceDataLine.close();
}


/** /**
* Converts float audio array to an interleaved audio array of 16-bit samples * Converts a float audio array [-1,1] to an interleaved array of 16-bit samples
* in little-endian (low-byte, high-byte) format.
*/ */
private byte[] floatsToBytes(float[][] fData, byte[] bData) { private byte[] floatsToBytes(float[][] fData, byte[] bData) {
int index = 0; int index = 0;
Expand All @@ -80,70 +87,11 @@ private byte[] floatsToBytes(float[][] fData, byte[] bData) {
} }
return bData; return bData;
} }


public void stopAudio() {
keepRunning = false;
}

public void addJVstHost(JVstHost vst) {
this.vst = vst;
fOutputs = new float[vst.numOutputs()][vst.getBlockSize()];
bOutput = new byte[vst.numOutputs() * vst.getBlockSize() * 2];
}

// midi events are collected and passed into the jvsthost every time
// processReplacing gets called
public synchronized void addMidiMessages(ShortMessage message) {
pendingMidi.add(message);
}

public synchronized ShortMessage[] getMidiMessages() {
ShortMessage[] messages = pendingMidi.toArray(new ShortMessage[0]);
pendingMidi.clear();
return messages;
}

public void run() { public void run() {
keepRunning = true; while (true) {
while (keepRunning) {
vst.setMidiEvents(getMidiMessages()); // pass in the midi event
vst.processReplacing(fOutputs); vst.processReplacing(fOutputs);
sourceDataLine.write(floatsToBytes(fOutputs, bOutput), 0, bOutput.length); sourceDataLine.write(floatsToBytes(fOutputs, bOutput), 0, bOutput.length);
} }
sourceDataLine.drain();
sourceDataLine.close();
} }

/**
* Play the given array once, independent of the real-time audio thread.
* TODO: implement!
* @param fInputs
*/
@Deprecated
public void playOnce(float[][] fInputs) {
DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, audioFormat);
final int localBlockSize = 1024;
sourceDataLine = null;
try {
sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
sourceDataLine.open(audioFormat, localBlockSize);
sourceDataLine.start();
} catch (LineUnavailableException lue) {
lue.printStackTrace(System.err);
System.exit(1);
}
byte[] bInputs = new byte[fInputs.length * fInputs[0].length * 2];
floatsToBytes(fInputs, bInputs);
/*
Thread thread = new Thread() {
public void run() {
int bytesToWrite
while
sourceDataLine.write(bInputs, 0, bOutput.length);
}
};
thread.start();
*/
}

} }

0 comments on commit 10f57d4

Please sign in to comment.