Chris@117: #!/usr/bin/env python Chris@117: Chris@117: # Python Vamp Host Chris@117: # Copyright (c) 2008-2015 Queen Mary, University of London Chris@117: # Chris@117: # Permission is hereby granted, free of charge, to any person Chris@117: # obtaining a copy of this software and associated documentation Chris@117: # files (the "Software"), to deal in the Software without Chris@117: # restriction, including without limitation the rights to use, copy, Chris@117: # modify, merge, publish, distribute, sublicense, and/or sell copies Chris@117: # of the Software, and to permit persons to whom the Software is Chris@117: # furnished to do so, subject to the following conditions: Chris@117: # Chris@117: # The above copyright notice and this permission notice shall be Chris@117: # included in all copies or substantial portions of the Software. Chris@117: # Chris@117: # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, Chris@117: # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF Chris@117: # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND Chris@117: # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY Chris@117: # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF Chris@117: # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION Chris@117: # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Chris@117: # Chris@117: # Except as contained in this notice, the names of the Centre for Chris@117: # Digital Music and Queen Mary, University of London shall not be Chris@117: # used in advertising or otherwise to promote the sale, use or other Chris@117: # dealings in this Software without prior written authorization. Chris@117: Chris@56: '''A high-level interface to the vampyhost extension module, for quickly and easily running Vamp audio analysis plugins on audio files and buffers.''' Chris@56: Chris@56: import vampyhost Chris@112: import vamp.frames Chris@112: import vamp.load Chris@68: Chris@100: def process_with_initialised_plugin(ff, sample_rate, step_size, plugin, outputs): Chris@89: Chris@98: out_indices = dict([(id, plugin.get_output(id)["output_index"]) Chris@98: for id in outputs]) Chris@89: plugin.reset() Chris@89: fi = 0 Chris@89: Chris@89: for f in ff: Chris@89: timestamp = vampyhost.frame_to_realtime(fi, sample_rate) Chris@89: results = plugin.process_block(f, timestamp) Chris@89: # results is a dict mapping output number -> list of feature dicts Chris@89: for o in outputs: Chris@89: ix = out_indices[o] Chris@89: if ix in results: Chris@89: for r in results[ix]: Chris@89: yield { o: r } Chris@89: fi = fi + step_size Chris@89: Chris@89: results = plugin.get_remaining_features() Chris@89: for o in outputs: Chris@89: ix = out_indices[o] Chris@89: if ix in results: Chris@89: for r in results[ix]: Chris@89: yield { o: r } Chris@89: Chris@89: Chris@140: def process_audio(data, sample_rate, plugin_key, output = "", parameters = {}, **kwargs): Chris@98: """Process audio data with a Vamp plugin, and make the results from a Chris@98: single plugin output available as a generator. Chris@98: Chris@98: The provided data should be a 1- or 2-dimensional list or NumPy Chris@98: array of floats. If it is 2-dimensional, the first dimension is Chris@98: taken to be the channel count. Chris@98: Chris@98: The returned results will be those calculated by the plugin with Chris@98: the given key and returned through its output with the given Chris@98: output identifier. If the requested output is the empty string, Chris@98: the first output provided by the plugin will be used. Chris@98: Chris@98: If the parameters dict is non-empty, the plugin will be configured Chris@98: by setting its parameters according to the (string) key and Chris@98: (float) value data found in the dict. Chris@98: Chris@98: This function acts as a generator, yielding a sequence of result Chris@98: features as it obtains them. Each feature is represented as a Chris@98: dictionary containing, optionally, timestamp and duration Chris@98: (RealTime objects), label (string), and a 1-dimensional array of Chris@98: float values. Chris@99: Chris@143: If you wish to override the step size, block size, or process Chris@143: timestamp method to be used, you may supply them as keyword Chris@143: arguments with keywords step_size (int), block_size (int), and Chris@143: process_timestamp_method (choose from vamp.vampyhost.SHIFT_DATA, Chris@143: vamp.vampyhost.SHIFT_TIMESTAMP, or vamp.vampyhost.NO_SHIFT). Chris@143: Chris@99: If you would prefer to obtain all features in a single output Chris@99: structure, consider using vamp.collect() instead. Chris@98: """ Chris@89: Chris@140: plugin, step_size, block_size = vamp.load.load_and_configure(data, sample_rate, plugin_key, parameters, **kwargs) Chris@89: Chris@89: if output == "": Chris@89: output = plugin.get_output(0)["identifier"] Chris@89: Chris@112: ff = vamp.frames.frames_from_array(data, step_size, block_size) Chris@89: Chris@100: for r in process_with_initialised_plugin(ff, sample_rate, step_size, plugin, [output]): Chris@89: yield r[output] Chris@76: Chris@89: plugin.unload() Chris@89: Chris@76: Chris@129: def process_frames(ff, sample_rate, step_size, plugin_key, output = "", parameters = {}): Chris@98: """Process audio data with a Vamp plugin, and make the results from a Chris@98: single plugin output available as a generator. Chris@95: Chris@98: The provided data should be an enumerable sequence of time-domain Chris@98: audio frames, of which each frame is 2-dimensional list or NumPy Chris@98: array of floats. The first dimension is taken to be the channel Chris@98: count, and the second dimension the frame or block size. The Chris@98: step_size argument gives the increment in audio samples from one Chris@98: frame to the next. Each frame must have the same size. Chris@95: Chris@98: The returned results will be those calculated by the plugin with Chris@98: the given key and returned through its output with the given Chris@98: output identifier. If the requested output is the empty string, Chris@98: the first output provided by the plugin will be used. Chris@95: Chris@98: If the parameters dict is non-empty, the plugin will be configured Chris@98: by setting its parameters according to the (string) key and Chris@98: (float) value data found in the dict. Chris@98: Chris@98: This function acts as a generator, yielding a sequence of result Chris@98: features as it obtains them. Each feature is represented as a Chris@98: dictionary containing, optionally, timestamp and duration Chris@98: (RealTime objects), label (string), and a 1-dimensional array of Chris@98: float values. Chris@99: Chris@99: If you would prefer to obtain all features in a single output Chris@99: structure, consider using vamp.collect() instead. Chris@98: """ Chris@129: plugin = vampyhost.load_plugin(plugin_key, sample_rate, Chris@98: vampyhost.ADAPT_INPUT_DOMAIN + Chris@98: vampyhost.ADAPT_BUFFER_SIZE + Chris@98: vampyhost.ADAPT_CHANNEL_COUNT) Chris@98: Chris@98: fi = 0 Chris@98: channels = 0 Chris@98: block_size = 0 Chris@98: Chris@98: if output == "": Chris@98: out_index = 0 Chris@98: else: Chris@98: out_index = plugin.get_output(output)["output_index"] Chris@95: Chris@98: for f in ff: Chris@95: Chris@98: if fi == 0: Chris@98: channels = f.shape[0] Chris@98: block_size = f.shape[1] Chris@98: plugin.set_parameter_values(parameters) Chris@98: if not plugin.initialise(channels, step_size, block_size): Chris@98: raise "Failed to initialise plugin" Chris@98: Chris@98: timestamp = vampyhost.frame_to_realtime(fi, sample_rate) Chris@98: results = plugin.process_block(f, timestamp) Chris@98: # results is a dict mapping output number -> list of feature dicts Chris@98: if out_index in results: Chris@98: for r in results[out_index]: Chris@98: yield r Chris@98: Chris@98: fi = fi + step_size Chris@98: Chris@98: if fi > 0: Chris@98: results = plugin.get_remaining_features() Chris@98: if out_index in results: Chris@98: for r in results[out_index]: Chris@98: yield r Chris@98: Chris@95: plugin.unload() Chris@95: Chris@95: Chris@140: def process_audio_multiple_outputs(data, sample_rate, plugin_key, outputs, parameters = {}, **kwargs): Chris@99: """Process audio data with a Vamp plugin, and make the results from a Chris@99: set of plugin outputs available as a generator. Chris@99: Chris@99: The provided data should be a 1- or 2-dimensional list or NumPy Chris@99: array of floats. If it is 2-dimensional, the first dimension is Chris@99: taken to be the channel count. Chris@99: Chris@99: The returned results will be those calculated by the plugin with Chris@99: the given key and returned through its outputs whose identifiers Chris@99: are given in the outputs argument. Chris@99: Chris@99: If the parameters dict is non-empty, the plugin will be configured Chris@99: by setting its parameters according to the (string) key and Chris@99: (float) value data found in the dict. Chris@99: Chris@99: This function acts as a generator, yielding a sequence of result Chris@99: feature sets as it obtains them. Each feature set is a dictionary Chris@99: mapping from output identifier to a list of features, each Chris@99: represented as a dictionary containing, optionally, timestamp and Chris@99: duration (RealTime objects), label (string), and a 1-dimensional Chris@99: array of float values. Chris@143: Chris@143: If you wish to override the step size, block size, or process Chris@143: timestamp method to be used, you may supply them as keyword Chris@143: arguments with keywords step_size (int), block_size (int), and Chris@143: process_timestamp_method (choose from vamp.vampyhost.SHIFT_DATA, Chris@143: vamp.vampyhost.SHIFT_TIMESTAMP, or vamp.vampyhost.NO_SHIFT). Chris@99: """ Chris@68: Chris@140: plugin, step_size, block_size = vamp.load.load_and_configure(data, sample_rate, plugin_key, parameters, **kwargs) Chris@64: Chris@112: ff = vamp.frames.frames_from_array(data, step_size, block_size) Chris@64: Chris@100: for r in process_with_initialised_plugin(ff, sample_rate, step_size, plugin, outputs): Chris@89: yield r Chris@61: Chris@89: plugin.unload() Chris@99: Chris@100: Chris@129: def process_frames_multiple_outputs(ff, sample_rate, step_size, plugin_key, outputs, parameters = {}): Chris@100: """Process audio data with a Vamp plugin, and make the results from a Chris@100: set of plugin outputs available as a generator. Chris@100: Chris@100: The provided data should be an enumerable sequence of time-domain Chris@100: audio frames, of which each frame is 2-dimensional list or NumPy Chris@100: array of floats. The first dimension is taken to be the channel Chris@100: count, and the second dimension the frame or block size. The Chris@100: step_size argument gives the increment in audio samples from one Chris@100: frame to the next. Each frame must have the same size. Chris@100: Chris@100: The returned results will be those calculated by the plugin with Chris@100: the given key and returned through its outputs whose identifiers Chris@100: are given in the outputs argument. Chris@100: Chris@100: If the parameters dict is non-empty, the plugin will be configured Chris@100: by setting its parameters according to the (string) key and Chris@100: (float) value data found in the dict. Chris@100: Chris@100: This function acts as a generator, yielding a sequence of result Chris@100: feature sets as it obtains them. Each feature set is a dictionary Chris@100: mapping from output identifier to a list of features, each Chris@100: represented as a dictionary containing, optionally, timestamp and Chris@100: duration (RealTime objects), label (string), and a 1-dimensional Chris@100: array of float values. Chris@100: """ Chris@129: plugin = vampyhost.load_plugin(plugin_key, sample_rate, Chris@100: vampyhost.ADAPT_INPUT_DOMAIN + Chris@100: vampyhost.ADAPT_BUFFER_SIZE + Chris@100: vampyhost.ADAPT_CHANNEL_COUNT) Chris@100: Chris@100: out_indices = dict([(id, plugin.get_output(id)["output_index"]) Chris@100: for id in outputs]) Chris@100: Chris@100: fi = 0 Chris@100: channels = 0 Chris@100: block_size = 0 Chris@100: Chris@100: for f in ff: Chris@100: Chris@100: if fi == 0: Chris@100: channels = f.shape[0] Chris@100: block_size = f.shape[1] Chris@100: plugin.set_parameter_values(parameters) Chris@100: if not plugin.initialise(channels, step_size, block_size): Chris@100: raise "Failed to initialise plugin" Chris@100: Chris@100: timestamp = vampyhost.frame_to_realtime(fi, sample_rate) Chris@100: results = plugin.process_block(f, timestamp) Chris@100: # results is a dict mapping output number -> list of feature dicts Chris@100: for o in outputs: Chris@100: ix = out_indices[o] Chris@100: if ix in results: Chris@100: for r in results[ix]: Chris@100: yield { o: r } Chris@100: fi = fi + step_size Chris@100: Chris@100: if fi > 0: Chris@100: results = plugin.get_remaining_features() Chris@100: for o in outputs: Chris@100: ix = out_indices[o] Chris@100: if ix in results: Chris@100: for r in results[ix]: Chris@100: yield { o: r } Chris@100: Chris@100: plugin.unload() Chris@100: Chris@100: