annotate host/host.java @ 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
children c9515589be7d
rev   line source
Chris@31 1
Chris@31 2 import java.util.ArrayList;
Chris@31 3 import java.util.TreeMap;
Chris@31 4 import java.util.Map;
Chris@31 5 import java.util.List;
Chris@31 6 import java.lang.RuntimeException;
Chris@31 7
Chris@31 8 import org.vamp_plugins.PluginLoader;
Chris@31 9 import org.vamp_plugins.Plugin;
Chris@31 10 import org.vamp_plugins.ParameterDescriptor;
Chris@31 11 import org.vamp_plugins.OutputDescriptor;
Chris@31 12 import org.vamp_plugins.Feature;
Chris@31 13 import org.vamp_plugins.RealTime;
Chris@31 14
Chris@31 15 import javax.sound.sampled.AudioSystem;
Chris@31 16 import javax.sound.sampled.AudioInputStream;
Chris@31 17 import javax.sound.sampled.AudioFormat;
Chris@31 18 import javax.sound.sampled.UnsupportedAudioFileException;
Chris@31 19
Chris@31 20 import java.io.File;
Chris@31 21 import java.io.IOException;
Chris@31 22
Chris@31 23 public class host
Chris@31 24 {
Chris@31 25 private static void printFeatures(RealTime frameTime, Integer output,
Chris@31 26 Map<Integer, ArrayList<Feature>> features)
Chris@31 27 {
Chris@31 28 if (!features.containsKey(output)) return;
Chris@31 29
Chris@31 30 for (Feature f : features.get(output)) {
Chris@31 31 if (f.hasTimestamp) {
Chris@31 32 System.out.print(f.timestamp);
Chris@31 33 } else {
Chris@31 34 System.out.print(frameTime);
Chris@31 35 }
Chris@31 36 if (f.hasDuration) {
Chris@31 37 System.out.print("," + f.duration);
Chris@31 38 }
Chris@31 39 System.out.print(":");
Chris@31 40 for (float v : f.values) {
Chris@31 41 System.out.print(" " + v);
Chris@31 42 }
Chris@31 43 System.out.print(" " + f.label);
Chris@31 44 System.out.println("");
Chris@31 45 }
Chris@31 46 }
Chris@31 47
Chris@31 48 private static void usage() {
Chris@31 49 System.err.println("Usage: host pluginlibrary:plugin:output file.wav");
Chris@31 50 }
Chris@31 51
Chris@31 52 private static int readBlock(AudioFormat format, AudioInputStream stream,
Chris@31 53 float[][] buffers)
Chris@31 54 throws java.io.IOException
Chris@31 55 {
Chris@31 56 // 16-bit LE signed PCM only
Chris@31 57 int channels = format.getChannels();
Chris@31 58 byte[] raw = new byte[buffers[0].length * channels * 2];
Chris@31 59 int read = stream.read(raw);
Chris@31 60 if (read < 0) return read;
Chris@31 61 int frames = read / (channels * 2);
Chris@31 62 for (int i = 0; i < frames; ++i) {
Chris@31 63 for (int c = 0; c < channels; ++c) {
Chris@31 64 int ix = i * channels + c;
Chris@31 65 int ival = (raw[ix*2] & 0xff) | (raw[ix*2 + 1] << 8);
Chris@31 66 float fval = ival / 32768.0f;
Chris@31 67 buffers[c][i] = fval;
Chris@31 68 }
Chris@31 69 }
Chris@31 70 return frames;
Chris@31 71 }
Chris@31 72
Chris@31 73 public static void main(String[] args)
Chris@31 74 {
Chris@31 75 if (args.length < 2) {
Chris@31 76 usage();
Chris@31 77 return;
Chris@31 78 }
Chris@31 79
Chris@31 80 PluginLoader loader = PluginLoader.getInstance();
Chris@31 81
Chris@31 82 String key = args[0];
Chris@31 83 String filename = args[1];
Chris@31 84
Chris@31 85 String[] keyparts = key.split(":");
Chris@31 86 if (keyparts.length < 3) {
Chris@31 87 usage();
Chris@31 88 return;
Chris@31 89 }
Chris@31 90
Chris@31 91 String pluginKey = keyparts[0] + ":" + keyparts[1];
Chris@31 92 String outputKey = keyparts[2];
Chris@31 93
Chris@31 94 try {
Chris@31 95 File f = new File(filename);
Chris@31 96 AudioInputStream stream = AudioSystem.getAudioInputStream(f);
Chris@31 97 AudioFormat format = stream.getFormat();
Chris@31 98
Chris@31 99 if (format.getSampleSizeInBits() != 16 ||
Chris@31 100 format.getEncoding() != AudioFormat.Encoding.PCM_SIGNED ||
Chris@31 101 format.isBigEndian()) {
Chris@31 102 System.err.println("Sorry, only 16-bit signed little-endian PCM files supported");
Chris@31 103 return;
Chris@31 104 }
Chris@31 105
Chris@31 106 float rate = format.getFrameRate();
Chris@31 107 int channels = format.getChannels();
Chris@31 108 int bytesPerFrame = format.getFrameSize();
Chris@31 109 int blockSize = 1024; // frames
Chris@31 110
Chris@31 111 Plugin p = loader.loadPlugin
Chris@31 112 (pluginKey, rate, PluginLoader.AdapterFlags.ADAPT_ALL);
Chris@31 113
Chris@31 114 OutputDescriptor[] outputs = p.getOutputDescriptors();
Chris@31 115 int outputNumber = -1;
Chris@31 116 for (int i = 0; i < outputs.length; ++i) {
Chris@31 117 if (outputs[i].identifier.equals(outputKey)) outputNumber = i;
Chris@31 118 }
Chris@31 119 if (outputNumber < 0) {
Chris@31 120 System.err.println("Plugin lacks output id: " + outputKey);
Chris@31 121 System.err.print("Outputs are:");
Chris@31 122 for (int i = 0; i < outputs.length; ++i) {
Chris@31 123 System.err.print(" " + outputs[i].identifier);
Chris@31 124 }
Chris@31 125 System.err.println("");
Chris@31 126 return;
Chris@31 127 }
Chris@31 128
Chris@31 129 boolean b = p.initialise(channels, blockSize, blockSize);
Chris@31 130 if (!b) {
Chris@31 131 System.err.println("Plugin initialise failed");
Chris@31 132 return;
Chris@31 133 }
Chris@31 134
Chris@31 135 float[][] buffers = new float[channels][blockSize];
Chris@31 136
Chris@31 137 boolean done = false;
Chris@31 138 boolean incomplete = false;
Chris@31 139 int block = 0;
Chris@31 140
Chris@31 141 while (!done) {
Chris@31 142
Chris@31 143 for (int c = 0; c < channels; ++c) {
Chris@31 144 for (int i = 0; i < blockSize; ++i) {
Chris@31 145 buffers[c][i] = 0.0f;
Chris@31 146 }
Chris@31 147 }
Chris@31 148
Chris@31 149 int read = readBlock(format, stream, buffers);
Chris@31 150
Chris@31 151 if (read < 0) {
Chris@31 152 done = true;
Chris@31 153 } else {
Chris@31 154
Chris@31 155 if (incomplete) {
Chris@31 156 // An incomplete block is only OK if it's the
Chris@31 157 // last one -- so if the previous block was
Chris@31 158 // incomplete, we have trouble
Chris@31 159 System.err.println("Audio file read incomplete! Short buffer detected at " + block * blockSize);
Chris@31 160 return;
Chris@31 161 }
Chris@31 162
Chris@31 163 incomplete = (read < buffers[0].length);
Chris@31 164
Chris@31 165 RealTime timestamp = RealTime.frame2RealTime
Chris@31 166 (block * blockSize, (int)(rate + 0.5));
Chris@31 167
Chris@31 168 TreeMap<Integer, ArrayList<Feature>>
Chris@31 169 features = p.process(buffers, timestamp);
Chris@31 170
Chris@31 171 printFeatures(timestamp, outputNumber, features);
Chris@31 172
Chris@31 173 timestamp.dispose();
Chris@31 174 }
Chris@31 175
Chris@31 176 ++block;
Chris@31 177 }
Chris@31 178
Chris@31 179 TreeMap<Integer, ArrayList<Feature>>
Chris@31 180 features = p.getRemainingFeatures();
Chris@31 181
Chris@31 182 RealTime timestamp = RealTime.frame2RealTime
Chris@31 183 (block * blockSize, (int)(rate + 0.5));
Chris@31 184 printFeatures(timestamp, outputNumber, features);
Chris@31 185 timestamp.dispose();
Chris@31 186
Chris@31 187 p.dispose();
Chris@31 188
Chris@31 189 } catch (java.io.IOException e) {
Chris@31 190 System.err.println("Failed to read audio file: " + e.getMessage());
Chris@31 191
Chris@31 192 } catch (javax.sound.sampled.UnsupportedAudioFileException e) {
Chris@31 193 System.err.println("Unsupported audio file format: " + e.getMessage());
Chris@31 194
Chris@31 195 } catch (PluginLoader.LoadFailedException e) {
Chris@31 196 System.err.println("Plugin load failed (unknown plugin?): key is " +
Chris@31 197 key);
Chris@31 198 }
Chris@31 199 }
Chris@31 200 }
Chris@31 201