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