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
|