Chris@117
|
1 #!/usr/bin/env python
|
Chris@117
|
2
|
Chris@117
|
3 # Python Vamp Host
|
Chris@117
|
4 # Copyright (c) 2008-2015 Queen Mary, University of London
|
Chris@117
|
5 #
|
Chris@117
|
6 # Permission is hereby granted, free of charge, to any person
|
Chris@117
|
7 # obtaining a copy of this software and associated documentation
|
Chris@117
|
8 # files (the "Software"), to deal in the Software without
|
Chris@117
|
9 # restriction, including without limitation the rights to use, copy,
|
Chris@117
|
10 # modify, merge, publish, distribute, sublicense, and/or sell copies
|
Chris@117
|
11 # of the Software, and to permit persons to whom the Software is
|
Chris@117
|
12 # furnished to do so, subject to the following conditions:
|
Chris@117
|
13 #
|
Chris@117
|
14 # The above copyright notice and this permission notice shall be
|
Chris@117
|
15 # included in all copies or substantial portions of the Software.
|
Chris@117
|
16 #
|
Chris@117
|
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
Chris@117
|
18 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
Chris@117
|
19 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
Chris@117
|
20 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
Chris@117
|
21 # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
Chris@117
|
22 # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
Chris@117
|
23 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
Chris@117
|
24 #
|
Chris@117
|
25 # Except as contained in this notice, the names of the Centre for
|
Chris@117
|
26 # Digital Music and Queen Mary, University of London shall not be
|
Chris@117
|
27 # used in advertising or otherwise to promote the sale, use or other
|
Chris@117
|
28 # dealings in this Software without prior written authorization.
|
Chris@117
|
29
|
Chris@56
|
30 '''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
|
31
|
Chris@56
|
32 import vampyhost
|
Chris@112
|
33 import vamp.frames
|
Chris@112
|
34 import vamp.load
|
Chris@68
|
35
|
Chris@100
|
36 def process_with_initialised_plugin(ff, sample_rate, step_size, plugin, outputs):
|
Chris@89
|
37
|
Chris@98
|
38 out_indices = dict([(id, plugin.get_output(id)["output_index"])
|
Chris@98
|
39 for id in outputs])
|
Chris@89
|
40 plugin.reset()
|
Chris@89
|
41 fi = 0
|
Chris@89
|
42
|
Chris@89
|
43 for f in ff:
|
Chris@89
|
44 timestamp = vampyhost.frame_to_realtime(fi, sample_rate)
|
Chris@89
|
45 results = plugin.process_block(f, timestamp)
|
Chris@89
|
46 # results is a dict mapping output number -> list of feature dicts
|
Chris@89
|
47 for o in outputs:
|
Chris@89
|
48 ix = out_indices[o]
|
Chris@89
|
49 if ix in results:
|
Chris@89
|
50 for r in results[ix]:
|
Chris@89
|
51 yield { o: r }
|
Chris@89
|
52 fi = fi + step_size
|
Chris@89
|
53
|
Chris@89
|
54 results = plugin.get_remaining_features()
|
Chris@89
|
55 for o in outputs:
|
Chris@89
|
56 ix = out_indices[o]
|
Chris@89
|
57 if ix in results:
|
Chris@89
|
58 for r in results[ix]:
|
Chris@89
|
59 yield { o: r }
|
Chris@89
|
60
|
Chris@89
|
61
|
Chris@112
|
62 def process_audio(data, sample_rate, key, output = "", parameters = {}):
|
Chris@98
|
63 """Process audio data with a Vamp plugin, and make the results from a
|
Chris@98
|
64 single plugin output available as a generator.
|
Chris@98
|
65
|
Chris@98
|
66 The provided data should be a 1- or 2-dimensional list or NumPy
|
Chris@98
|
67 array of floats. If it is 2-dimensional, the first dimension is
|
Chris@98
|
68 taken to be the channel count.
|
Chris@98
|
69
|
Chris@98
|
70 The returned results will be those calculated by the plugin with
|
Chris@98
|
71 the given key and returned through its output with the given
|
Chris@98
|
72 output identifier. If the requested output is the empty string,
|
Chris@98
|
73 the first output provided by the plugin will be used.
|
Chris@98
|
74
|
Chris@98
|
75 If the parameters dict is non-empty, the plugin will be configured
|
Chris@98
|
76 by setting its parameters according to the (string) key and
|
Chris@98
|
77 (float) value data found in the dict.
|
Chris@98
|
78
|
Chris@98
|
79 This function acts as a generator, yielding a sequence of result
|
Chris@98
|
80 features as it obtains them. Each feature is represented as a
|
Chris@98
|
81 dictionary containing, optionally, timestamp and duration
|
Chris@98
|
82 (RealTime objects), label (string), and a 1-dimensional array of
|
Chris@98
|
83 float values.
|
Chris@99
|
84
|
Chris@99
|
85 If you would prefer to obtain all features in a single output
|
Chris@99
|
86 structure, consider using vamp.collect() instead.
|
Chris@98
|
87 """
|
Chris@89
|
88
|
Chris@112
|
89 plugin, step_size, block_size = vamp.load.load_and_configure(data, sample_rate, key, parameters)
|
Chris@89
|
90
|
Chris@89
|
91 if output == "":
|
Chris@89
|
92 output = plugin.get_output(0)["identifier"]
|
Chris@89
|
93
|
Chris@112
|
94 ff = vamp.frames.frames_from_array(data, step_size, block_size)
|
Chris@89
|
95
|
Chris@100
|
96 for r in process_with_initialised_plugin(ff, sample_rate, step_size, plugin, [output]):
|
Chris@89
|
97 yield r[output]
|
Chris@76
|
98
|
Chris@89
|
99 plugin.unload()
|
Chris@89
|
100
|
Chris@76
|
101
|
Chris@98
|
102 def process_frames(ff, sample_rate, step_size, key, output = "", parameters = {}):
|
Chris@98
|
103 """Process audio data with a Vamp plugin, and make the results from a
|
Chris@98
|
104 single plugin output available as a generator.
|
Chris@95
|
105
|
Chris@98
|
106 The provided data should be an enumerable sequence of time-domain
|
Chris@98
|
107 audio frames, of which each frame is 2-dimensional list or NumPy
|
Chris@98
|
108 array of floats. The first dimension is taken to be the channel
|
Chris@98
|
109 count, and the second dimension the frame or block size. The
|
Chris@98
|
110 step_size argument gives the increment in audio samples from one
|
Chris@98
|
111 frame to the next. Each frame must have the same size.
|
Chris@95
|
112
|
Chris@98
|
113 The returned results will be those calculated by the plugin with
|
Chris@98
|
114 the given key and returned through its output with the given
|
Chris@98
|
115 output identifier. If the requested output is the empty string,
|
Chris@98
|
116 the first output provided by the plugin will be used.
|
Chris@95
|
117
|
Chris@98
|
118 If the parameters dict is non-empty, the plugin will be configured
|
Chris@98
|
119 by setting its parameters according to the (string) key and
|
Chris@98
|
120 (float) value data found in the dict.
|
Chris@98
|
121
|
Chris@98
|
122 This function acts as a generator, yielding a sequence of result
|
Chris@98
|
123 features as it obtains them. Each feature is represented as a
|
Chris@98
|
124 dictionary containing, optionally, timestamp and duration
|
Chris@98
|
125 (RealTime objects), label (string), and a 1-dimensional array of
|
Chris@98
|
126 float values.
|
Chris@99
|
127
|
Chris@99
|
128 If you would prefer to obtain all features in a single output
|
Chris@99
|
129 structure, consider using vamp.collect() instead.
|
Chris@98
|
130 """
|
Chris@98
|
131 plugin = vampyhost.load_plugin(key, sample_rate,
|
Chris@98
|
132 vampyhost.ADAPT_INPUT_DOMAIN +
|
Chris@98
|
133 vampyhost.ADAPT_BUFFER_SIZE +
|
Chris@98
|
134 vampyhost.ADAPT_CHANNEL_COUNT)
|
Chris@98
|
135
|
Chris@98
|
136 fi = 0
|
Chris@98
|
137 channels = 0
|
Chris@98
|
138 block_size = 0
|
Chris@98
|
139
|
Chris@98
|
140 if output == "":
|
Chris@98
|
141 out_index = 0
|
Chris@98
|
142 else:
|
Chris@98
|
143 out_index = plugin.get_output(output)["output_index"]
|
Chris@95
|
144
|
Chris@98
|
145 for f in ff:
|
Chris@95
|
146
|
Chris@98
|
147 if fi == 0:
|
Chris@98
|
148 channels = f.shape[0]
|
Chris@98
|
149 block_size = f.shape[1]
|
Chris@98
|
150 plugin.set_parameter_values(parameters)
|
Chris@98
|
151 if not plugin.initialise(channels, step_size, block_size):
|
Chris@98
|
152 raise "Failed to initialise plugin"
|
Chris@98
|
153
|
Chris@98
|
154 timestamp = vampyhost.frame_to_realtime(fi, sample_rate)
|
Chris@98
|
155 results = plugin.process_block(f, timestamp)
|
Chris@98
|
156 # results is a dict mapping output number -> list of feature dicts
|
Chris@98
|
157 if out_index in results:
|
Chris@98
|
158 for r in results[out_index]:
|
Chris@98
|
159 yield r
|
Chris@98
|
160
|
Chris@98
|
161 fi = fi + step_size
|
Chris@98
|
162
|
Chris@98
|
163 if fi > 0:
|
Chris@98
|
164 results = plugin.get_remaining_features()
|
Chris@98
|
165 if out_index in results:
|
Chris@98
|
166 for r in results[out_index]:
|
Chris@98
|
167 yield r
|
Chris@98
|
168
|
Chris@95
|
169 plugin.unload()
|
Chris@95
|
170
|
Chris@95
|
171
|
Chris@117
|
172 def process_audio_multiple_outputs(data, sample_rate, key, outputs, parameters = {}):
|
Chris@99
|
173 """Process audio data with a Vamp plugin, and make the results from a
|
Chris@99
|
174 set of plugin outputs available as a generator.
|
Chris@99
|
175
|
Chris@99
|
176 The provided data should be a 1- or 2-dimensional list or NumPy
|
Chris@99
|
177 array of floats. If it is 2-dimensional, the first dimension is
|
Chris@99
|
178 taken to be the channel count.
|
Chris@99
|
179
|
Chris@99
|
180 The returned results will be those calculated by the plugin with
|
Chris@99
|
181 the given key and returned through its outputs whose identifiers
|
Chris@99
|
182 are given in the outputs argument.
|
Chris@99
|
183
|
Chris@99
|
184 If the parameters dict is non-empty, the plugin will be configured
|
Chris@99
|
185 by setting its parameters according to the (string) key and
|
Chris@99
|
186 (float) value data found in the dict.
|
Chris@99
|
187
|
Chris@99
|
188 This function acts as a generator, yielding a sequence of result
|
Chris@99
|
189 feature sets as it obtains them. Each feature set is a dictionary
|
Chris@99
|
190 mapping from output identifier to a list of features, each
|
Chris@99
|
191 represented as a dictionary containing, optionally, timestamp and
|
Chris@99
|
192 duration (RealTime objects), label (string), and a 1-dimensional
|
Chris@99
|
193 array of float values.
|
Chris@99
|
194 """
|
Chris@68
|
195
|
Chris@112
|
196 plugin, step_size, block_size = vamp.load.load_and_configure(data, sample_rate, key, parameters)
|
Chris@64
|
197
|
Chris@112
|
198 ff = vamp.frames.frames_from_array(data, step_size, block_size)
|
Chris@64
|
199
|
Chris@100
|
200 for r in process_with_initialised_plugin(ff, sample_rate, step_size, plugin, outputs):
|
Chris@89
|
201 yield r
|
Chris@61
|
202
|
Chris@89
|
203 plugin.unload()
|
Chris@99
|
204
|
Chris@100
|
205
|
Chris@100
|
206 def process_frames_multiple_outputs(ff, sample_rate, step_size, key, outputs, parameters = {}):
|
Chris@100
|
207 """Process audio data with a Vamp plugin, and make the results from a
|
Chris@100
|
208 set of plugin outputs available as a generator.
|
Chris@100
|
209
|
Chris@100
|
210 The provided data should be an enumerable sequence of time-domain
|
Chris@100
|
211 audio frames, of which each frame is 2-dimensional list or NumPy
|
Chris@100
|
212 array of floats. The first dimension is taken to be the channel
|
Chris@100
|
213 count, and the second dimension the frame or block size. The
|
Chris@100
|
214 step_size argument gives the increment in audio samples from one
|
Chris@100
|
215 frame to the next. Each frame must have the same size.
|
Chris@100
|
216
|
Chris@100
|
217 The returned results will be those calculated by the plugin with
|
Chris@100
|
218 the given key and returned through its outputs whose identifiers
|
Chris@100
|
219 are given in the outputs argument.
|
Chris@100
|
220
|
Chris@100
|
221 If the parameters dict is non-empty, the plugin will be configured
|
Chris@100
|
222 by setting its parameters according to the (string) key and
|
Chris@100
|
223 (float) value data found in the dict.
|
Chris@100
|
224
|
Chris@100
|
225 This function acts as a generator, yielding a sequence of result
|
Chris@100
|
226 feature sets as it obtains them. Each feature set is a dictionary
|
Chris@100
|
227 mapping from output identifier to a list of features, each
|
Chris@100
|
228 represented as a dictionary containing, optionally, timestamp and
|
Chris@100
|
229 duration (RealTime objects), label (string), and a 1-dimensional
|
Chris@100
|
230 array of float values.
|
Chris@100
|
231 """
|
Chris@100
|
232 plugin = vampyhost.load_plugin(key, sample_rate,
|
Chris@100
|
233 vampyhost.ADAPT_INPUT_DOMAIN +
|
Chris@100
|
234 vampyhost.ADAPT_BUFFER_SIZE +
|
Chris@100
|
235 vampyhost.ADAPT_CHANNEL_COUNT)
|
Chris@100
|
236
|
Chris@100
|
237 out_indices = dict([(id, plugin.get_output(id)["output_index"])
|
Chris@100
|
238 for id in outputs])
|
Chris@100
|
239
|
Chris@100
|
240 fi = 0
|
Chris@100
|
241 channels = 0
|
Chris@100
|
242 block_size = 0
|
Chris@100
|
243
|
Chris@100
|
244 for f in ff:
|
Chris@100
|
245
|
Chris@100
|
246 if fi == 0:
|
Chris@100
|
247 channels = f.shape[0]
|
Chris@100
|
248 block_size = f.shape[1]
|
Chris@100
|
249 plugin.set_parameter_values(parameters)
|
Chris@100
|
250 if not plugin.initialise(channels, step_size, block_size):
|
Chris@100
|
251 raise "Failed to initialise plugin"
|
Chris@100
|
252
|
Chris@100
|
253 timestamp = vampyhost.frame_to_realtime(fi, sample_rate)
|
Chris@100
|
254 results = plugin.process_block(f, timestamp)
|
Chris@100
|
255 # results is a dict mapping output number -> list of feature dicts
|
Chris@100
|
256 for o in outputs:
|
Chris@100
|
257 ix = out_indices[o]
|
Chris@100
|
258 if ix in results:
|
Chris@100
|
259 for r in results[ix]:
|
Chris@100
|
260 yield { o: r }
|
Chris@100
|
261 fi = fi + step_size
|
Chris@100
|
262
|
Chris@100
|
263 if fi > 0:
|
Chris@100
|
264 results = plugin.get_remaining_features()
|
Chris@100
|
265 for o in outputs:
|
Chris@100
|
266 ix = out_indices[o]
|
Chris@100
|
267 if ix in results:
|
Chris@100
|
268 for r in results[ix]:
|
Chris@100
|
269 yield { o: r }
|
Chris@100
|
270
|
Chris@100
|
271 plugin.unload()
|
Chris@100
|
272
|
Chris@100
|
273
|