Mercurial > hg > jvamp
changeset 31:d8ff8c5ad52a
Add host program, working like a minimal version of vamp-simple-host
author | Chris Cannam |
---|---|
date | Thu, 22 Nov 2012 14:31:23 +0000 |
parents | 02db37c2301b |
children | 993fc67495b6 |
files | host/host.java |
diffstat | 1 files changed, 201 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/host/host.java Thu Nov 22 14:31:23 2012 +0000 @@ -0,0 +1,201 @@ + +import java.util.ArrayList; +import java.util.TreeMap; +import java.util.Map; +import java.util.List; +import java.lang.RuntimeException; + +import org.vamp_plugins.PluginLoader; +import org.vamp_plugins.Plugin; +import org.vamp_plugins.ParameterDescriptor; +import org.vamp_plugins.OutputDescriptor; +import org.vamp_plugins.Feature; +import org.vamp_plugins.RealTime; + +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.UnsupportedAudioFileException; + +import java.io.File; +import java.io.IOException; + +public class host +{ + private static void printFeatures(RealTime frameTime, Integer output, + Map<Integer, ArrayList<Feature>> features) + { + if (!features.containsKey(output)) return; + + for (Feature f : features.get(output)) { + if (f.hasTimestamp) { + System.out.print(f.timestamp); + } else { + System.out.print(frameTime); + } + if (f.hasDuration) { + System.out.print("," + f.duration); + } + System.out.print(":"); + for (float v : f.values) { + System.out.print(" " + v); + } + System.out.print(" " + f.label); + System.out.println(""); + } + } + + private static void usage() { + System.err.println("Usage: host pluginlibrary:plugin:output file.wav"); + } + + private static int readBlock(AudioFormat format, AudioInputStream stream, + float[][] buffers) + throws java.io.IOException + { + // 16-bit LE signed PCM only + int channels = format.getChannels(); + byte[] raw = new byte[buffers[0].length * channels * 2]; + int read = stream.read(raw); + if (read < 0) return read; + int frames = read / (channels * 2); + for (int i = 0; i < frames; ++i) { + for (int c = 0; c < channels; ++c) { + int ix = i * channels + c; + int ival = (raw[ix*2] & 0xff) | (raw[ix*2 + 1] << 8); + float fval = ival / 32768.0f; + buffers[c][i] = fval; + } + } + return frames; + } + + public static void main(String[] args) + { + if (args.length < 2) { + usage(); + return; + } + + PluginLoader loader = PluginLoader.getInstance(); + + String key = args[0]; + String filename = args[1]; + + String[] keyparts = key.split(":"); + if (keyparts.length < 3) { + usage(); + return; + } + + String pluginKey = keyparts[0] + ":" + keyparts[1]; + String outputKey = keyparts[2]; + + try { + File f = new File(filename); + AudioInputStream stream = AudioSystem.getAudioInputStream(f); + AudioFormat format = stream.getFormat(); + + if (format.getSampleSizeInBits() != 16 || + format.getEncoding() != AudioFormat.Encoding.PCM_SIGNED || + format.isBigEndian()) { + System.err.println("Sorry, only 16-bit signed little-endian PCM files supported"); + return; + } + + float rate = format.getFrameRate(); + int channels = format.getChannels(); + int bytesPerFrame = format.getFrameSize(); + int blockSize = 1024; // frames + + Plugin p = loader.loadPlugin + (pluginKey, rate, PluginLoader.AdapterFlags.ADAPT_ALL); + + OutputDescriptor[] outputs = p.getOutputDescriptors(); + int outputNumber = -1; + for (int i = 0; i < outputs.length; ++i) { + if (outputs[i].identifier.equals(outputKey)) outputNumber = i; + } + if (outputNumber < 0) { + System.err.println("Plugin lacks output id: " + outputKey); + System.err.print("Outputs are:"); + for (int i = 0; i < outputs.length; ++i) { + System.err.print(" " + outputs[i].identifier); + } + System.err.println(""); + return; + } + + boolean b = p.initialise(channels, blockSize, blockSize); + if (!b) { + System.err.println("Plugin initialise failed"); + return; + } + + float[][] buffers = new float[channels][blockSize]; + + boolean done = false; + boolean incomplete = false; + int block = 0; + + while (!done) { + + for (int c = 0; c < channels; ++c) { + for (int i = 0; i < blockSize; ++i) { + buffers[c][i] = 0.0f; + } + } + + int read = readBlock(format, stream, buffers); + + if (read < 0) { + done = true; + } else { + + if (incomplete) { + // An incomplete block is only OK if it's the + // last one -- so if the previous block was + // incomplete, we have trouble + System.err.println("Audio file read incomplete! Short buffer detected at " + block * blockSize); + return; + } + + incomplete = (read < buffers[0].length); + + RealTime timestamp = RealTime.frame2RealTime + (block * blockSize, (int)(rate + 0.5)); + + TreeMap<Integer, ArrayList<Feature>> + features = p.process(buffers, timestamp); + + printFeatures(timestamp, outputNumber, features); + + timestamp.dispose(); + } + + ++block; + } + + TreeMap<Integer, ArrayList<Feature>> + features = p.getRemainingFeatures(); + + RealTime timestamp = RealTime.frame2RealTime + (block * blockSize, (int)(rate + 0.5)); + printFeatures(timestamp, outputNumber, features); + timestamp.dispose(); + + p.dispose(); + + } catch (java.io.IOException e) { + System.err.println("Failed to read audio file: " + e.getMessage()); + + } catch (javax.sound.sampled.UnsupportedAudioFileException e) { + System.err.println("Unsupported audio file format: " + e.getMessage()); + + } catch (PluginLoader.LoadFailedException e) { + System.err.println("Plugin load failed (unknown plugin?): key is " + + key); + } + } +} +