Chris@37: /* Chris@37: jVamp Chris@37: Chris@37: A Java host interface for Vamp audio analysis plugins Chris@37: Chris@37: Centre for Digital Music, Queen Mary, University of London. Chris@37: Copyright 2012 Chris Cannam and QMUL. Chris@37: Chris@37: Permission is hereby granted, free of charge, to any person Chris@37: obtaining a copy of this software and associated documentation Chris@37: files (the "Software"), to deal in the Software without Chris@37: restriction, including without limitation the rights to use, copy, Chris@37: modify, merge, publish, distribute, sublicense, and/or sell copies Chris@37: of the Software, and to permit persons to whom the Software is Chris@37: furnished to do so, subject to the following conditions: Chris@37: Chris@37: The above copyright notice and this permission notice shall be Chris@37: included in all copies or substantial portions of the Software. Chris@37: Chris@37: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, Chris@37: EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF Chris@37: MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND Chris@37: NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR Chris@37: ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF Chris@37: CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION Chris@37: WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Chris@37: Chris@37: Except as contained in this notice, the names of the Centre for Chris@37: Digital Music; Queen Mary, University of London; and Chris Cannam Chris@37: shall not be used in advertising or otherwise to promote the sale, Chris@37: use or other dealings in this Software without prior written Chris@37: authorization. Chris@37: */ Chris@31: Chris@43: import java.util.List; Chris@31: import java.util.TreeMap; Chris@31: import java.util.Map; Chris@31: import java.util.List; Chris@31: import java.lang.RuntimeException; Chris@31: Chris@31: import org.vamp_plugins.PluginLoader; Chris@31: import org.vamp_plugins.Plugin; Chris@31: import org.vamp_plugins.ParameterDescriptor; Chris@31: import org.vamp_plugins.OutputDescriptor; Chris@31: import org.vamp_plugins.Feature; Chris@31: import org.vamp_plugins.RealTime; Chris@31: Chris@31: import javax.sound.sampled.AudioSystem; Chris@31: import javax.sound.sampled.AudioInputStream; Chris@31: import javax.sound.sampled.AudioFormat; Chris@31: import javax.sound.sampled.UnsupportedAudioFileException; Chris@31: Chris@31: import java.io.File; Chris@31: import java.io.IOException; Chris@31: Chris@31: public class host Chris@31: { Chris@31: private static void printFeatures(RealTime frameTime, Integer output, Chris@43: Map> features) Chris@31: { Chris@31: if (!features.containsKey(output)) return; Chris@31: Chris@31: for (Feature f : features.get(output)) { Chris@31: if (f.hasTimestamp) { Chris@31: System.out.print(f.timestamp); Chris@31: } else { Chris@31: System.out.print(frameTime); Chris@31: } Chris@31: if (f.hasDuration) { Chris@31: System.out.print("," + f.duration); Chris@31: } Chris@31: System.out.print(":"); Chris@31: for (float v : f.values) { Chris@31: System.out.print(" " + v); Chris@31: } Chris@31: System.out.print(" " + f.label); Chris@31: System.out.println(""); Chris@31: } Chris@31: } Chris@31: Chris@31: private static void usage() { Chris@31: System.err.println("Usage: host pluginlibrary:plugin:output file.wav"); Chris@31: } Chris@31: Chris@31: private static int readBlock(AudioFormat format, AudioInputStream stream, Chris@31: float[][] buffers) Chris@31: throws java.io.IOException Chris@31: { Chris@31: // 16-bit LE signed PCM only Chris@31: int channels = format.getChannels(); Chris@31: byte[] raw = new byte[buffers[0].length * channels * 2]; Chris@31: int read = stream.read(raw); Chris@31: if (read < 0) return read; Chris@31: int frames = read / (channels * 2); Chris@31: for (int i = 0; i < frames; ++i) { Chris@31: for (int c = 0; c < channels; ++c) { Chris@31: int ix = i * channels + c; Chris@31: int ival = (raw[ix*2] & 0xff) | (raw[ix*2 + 1] << 8); Chris@31: float fval = ival / 32768.0f; Chris@31: buffers[c][i] = fval; Chris@31: } Chris@31: } Chris@31: return frames; Chris@31: } Chris@31: Chris@31: public static void main(String[] args) Chris@31: { Chris@31: if (args.length < 2) { Chris@31: usage(); Chris@31: return; Chris@31: } Chris@31: Chris@31: PluginLoader loader = PluginLoader.getInstance(); Chris@31: Chris@31: String key = args[0]; Chris@31: String filename = args[1]; Chris@31: Chris@31: String[] keyparts = key.split(":"); Chris@31: if (keyparts.length < 3) { Chris@31: usage(); Chris@31: return; Chris@31: } Chris@31: Chris@31: String pluginKey = keyparts[0] + ":" + keyparts[1]; Chris@31: String outputKey = keyparts[2]; Chris@31: Chris@31: try { Chris@31: File f = new File(filename); Chris@31: AudioInputStream stream = AudioSystem.getAudioInputStream(f); Chris@31: AudioFormat format = stream.getFormat(); Chris@31: Chris@31: if (format.getSampleSizeInBits() != 16 || Chris@31: format.getEncoding() != AudioFormat.Encoding.PCM_SIGNED || Chris@31: format.isBigEndian()) { Chris@31: System.err.println("Sorry, only 16-bit signed little-endian PCM files supported"); Chris@31: return; Chris@31: } Chris@31: Chris@31: float rate = format.getFrameRate(); Chris@31: int channels = format.getChannels(); Chris@31: int bytesPerFrame = format.getFrameSize(); Chris@31: int blockSize = 1024; // frames Chris@31: Chris@31: Plugin p = loader.loadPlugin Chris@31: (pluginKey, rate, PluginLoader.AdapterFlags.ADAPT_ALL); Chris@31: Chris@31: OutputDescriptor[] outputs = p.getOutputDescriptors(); Chris@31: int outputNumber = -1; Chris@31: for (int i = 0; i < outputs.length; ++i) { Chris@31: if (outputs[i].identifier.equals(outputKey)) outputNumber = i; Chris@31: } Chris@31: if (outputNumber < 0) { Chris@31: System.err.println("Plugin lacks output id: " + outputKey); Chris@31: System.err.print("Outputs are:"); Chris@31: for (int i = 0; i < outputs.length; ++i) { Chris@31: System.err.print(" " + outputs[i].identifier); Chris@31: } Chris@31: System.err.println(""); Chris@31: return; Chris@31: } Chris@31: Chris@31: boolean b = p.initialise(channels, blockSize, blockSize); Chris@31: if (!b) { Chris@31: System.err.println("Plugin initialise failed"); Chris@31: return; Chris@31: } Chris@31: Chris@31: float[][] buffers = new float[channels][blockSize]; Chris@31: Chris@31: boolean done = false; Chris@31: boolean incomplete = false; Chris@31: int block = 0; Chris@31: Chris@31: while (!done) { Chris@31: Chris@31: for (int c = 0; c < channels; ++c) { Chris@31: for (int i = 0; i < blockSize; ++i) { Chris@31: buffers[c][i] = 0.0f; Chris@31: } Chris@31: } Chris@31: Chris@31: int read = readBlock(format, stream, buffers); Chris@31: Chris@31: if (read < 0) { Chris@31: done = true; Chris@31: } else { Chris@31: Chris@31: if (incomplete) { Chris@31: // An incomplete block is only OK if it's the Chris@31: // last one -- so if the previous block was Chris@31: // incomplete, we have trouble Chris@31: System.err.println("Audio file read incomplete! Short buffer detected at " + block * blockSize); Chris@31: return; Chris@31: } Chris@31: Chris@31: incomplete = (read < buffers[0].length); Chris@31: Chris@31: RealTime timestamp = RealTime.frame2RealTime Chris@31: (block * blockSize, (int)(rate + 0.5)); Chris@31: Chris@43: Map> Chris@31: features = p.process(buffers, timestamp); Chris@31: Chris@31: printFeatures(timestamp, outputNumber, features); Chris@31: } Chris@31: Chris@31: ++block; Chris@31: } Chris@31: Chris@43: Map> Chris@31: features = p.getRemainingFeatures(); Chris@31: Chris@31: RealTime timestamp = RealTime.frame2RealTime Chris@31: (block * blockSize, (int)(rate + 0.5)); Chris@31: printFeatures(timestamp, outputNumber, features); Chris@31: Chris@31: p.dispose(); Chris@31: Chris@31: } catch (java.io.IOException e) { Chris@31: System.err.println("Failed to read audio file: " + e.getMessage()); Chris@31: Chris@31: } catch (javax.sound.sampled.UnsupportedAudioFileException e) { Chris@31: System.err.println("Unsupported audio file format: " + e.getMessage()); Chris@31: Chris@31: } catch (PluginLoader.LoadFailedException e) { Chris@31: System.err.println("Plugin load failed (unknown plugin?): key is " + Chris@31: key); Chris@31: } Chris@31: } Chris@31: } Chris@31: