Desirable goals » History » Version 3
Chris Cannam, 2015-01-12 12:18 PM
1 | 1 | Chris Cannam | h1. Desirable goals |
---|---|---|---|
2 | 1 | Chris Cannam | |
3 | 3 | Chris Cannam | *Dan writes:* |
4 | 1 | Chris Cannam | |
5 | 2 | Chris Cannam | _I would request that a function returning data block-by-block should be a "generator":https://wiki.python.org/moin/Generators which would make |
6 | 2 | Chris Cannam | it very flexible, and could also be used to avoid having to have all the data in memory at once._ |
7 | 2 | Chris Cannam | |
8 | 2 | Chris Cannam | ... |
9 | 2 | Chris Cannam | |
10 | 2 | Chris Cannam | _from my point of view, and not having thought through all the flexible types of output that vamp provides, I'd be hoping to type things like:_ |
11 | 1 | Chris Cannam | |
12 | 1 | Chris Cannam | <pre> |
13 | 1 | Chris Cannam | # all in-memory: |
14 | 1 | Chris Cannam | |
15 | 1 | Chris Cannam | plug = vh.loadPlugin('vamp-example-plugins:mfcc', 44100) |
16 | 1 | Chris Cannam | mfccs = np.array([features[0] for features in plug.processall([-0.1, 0, 0.1, 0, -0.1, 0, 0.1, 0])]) # this gives a 2D numpy array of shape [nframes, nmfccs] |
17 | 1 | Chris Cannam | |
18 | 1 | Chris Cannam | plug = vh.loadPlugin('vamp-example-plugins:onsetdetector', 44100) |
19 | 1 | Chris Cannam | onsets = np.array([features[0] for features in plug.processall([-0.1, 0, 0.1, 0, -0.1, 0, 0.1, 0])]) # this gives a 1D numpy array, a list of onset times I guess |
20 | 1 | Chris Cannam | |
21 | 1 | Chris Cannam | |
22 | 1 | Chris Cannam | # from disk, to memory: |
23 | 1 | Chris Cannam | |
24 | 1 | Chris Cannam | plug = vh.loadPlugin('vamp-example-plugins:mfcc', 44100) |
25 | 1 | Chris Cannam | with open(filepath) as f: |
26 | 1 | Chris Cannam | mfccs = np.array([features[0] for features in plug.processall(f)]) |
27 | 1 | Chris Cannam | plt.matshow(mfccs, interpolate='nearest') # a simple pyplot |
28 | 1 | Chris Cannam | |
29 | 1 | Chris Cannam | |
30 | 1 | Chris Cannam | # fully streaming: |
31 | 1 | Chris Cannam | |
32 | 1 | Chris Cannam | plug = vh.loadPlugin('vamp-example-plugins:mfcc', 44100) |
33 | 1 | Chris Cannam | with open(filepath) as f: |
34 | 1 | Chris Cannam | with open(outpath, 'wb') as outf: |
35 | 1 | Chris Cannam | for features in plug.processall(f): |
36 | 1 | Chris Cannam | outpath.write("%g\n" % features[0][0] ** 2) |
37 | 1 | Chris Cannam | </pre> |
38 | 1 | Chris Cannam | |
39 | 1 | Chris Cannam | _BTW I noticed I made a couple of mistakes in there_ |
40 | 3 | Chris Cannam | |
41 | 3 | Chris Cannam | ... |
42 | 3 | Chris Cannam | |
43 | 3 | Chris Cannam | *Chris writes:* |
44 | 3 | Chris Cannam | |
45 | 3 | Chris Cannam | API-wise, that's slightly problematic because the standard plugin interface goes |
46 | 3 | Chris Cannam | |
47 | 3 | Chris Cannam | <pre> |
48 | 3 | Chris Cannam | load -> initialise -> process, process, process |
49 | 3 | Chris Cannam | </pre> |
50 | 3 | Chris Cannam | |
51 | 3 | Chris Cannam | and one of the biggest problems is people forgetting / misunderstanding the initialise call. (Ignoring for now the merits or otherwise of the API design -- I just mean that this is the way it works now) |
52 | 3 | Chris Cannam | |
53 | 3 | Chris Cannam | So introducing another workflow that looks like |
54 | 3 | Chris Cannam | |
55 | 3 | Chris Cannam | <pre> |
56 | 3 | Chris Cannam | load -> processall |
57 | 3 | Chris Cannam | </pre> |
58 | 3 | Chris Cannam | |
59 | 3 | Chris Cannam | might be hazardous from that perspective. |
60 | 3 | Chris Cannam | |
61 | 3 | Chris Cannam | *Dan:* |
62 | 3 | Chris Cannam | |
63 | 3 | Chris Cannam | Note that the generator style means that it's still possible to do "process, process, process" if needed, for example: |
64 | 3 | Chris Cannam | |
65 | 3 | Chris Cannam | <pre> |
66 | 3 | Chris Cannam | mygenerator = plug.processall(f) |
67 | 3 | Chris Cannam | print mygenerator.next() # frame 0? |
68 | 3 | Chris Cannam | print mygenerator.next() # frame 1? |
69 | 3 | Chris Cannam | print mygenerator.next() # frame 2? |
70 | 3 | Chris Cannam | </pre> |
71 | 3 | Chris Cannam | |
72 | 3 | Chris Cannam | and the initialisation stuff is still bound up in there neatly rather than being forgettable. |
73 | 3 | Chris Cannam | |
74 | 3 | Chris Cannam | *Chris:* |
75 | 3 | Chris Cannam | |
76 | 3 | Chris Cannam | Also of course if you're running it on an audio file, it knows the sample rate / channel count etc so you should be able to short-circuit |
77 | 3 | Chris Cannam | it: |
78 | 3 | Chris Cannam | |
79 | 3 | Chris Cannam | <pre> |
80 | 3 | Chris Cannam | import vampyhost as vh |
81 | 3 | Chris Cannam | results = vh.processall(file, 'vamp-example-plugins:zerocrossings') |
82 | 3 | Chris Cannam | </pre> |
83 | 3 | Chris Cannam | |
84 | 3 | Chris Cannam | and it loads (& initialises) the plugin for you. |
85 | 3 | Chris Cannam | |
86 | 3 | Chris Cannam | *Dan:* |
87 | 3 | Chris Cannam | |
88 | 3 | Chris Cannam | Yes, feels good as high-level api |
89 | 3 | Chris Cannam | |
90 | 3 | Chris Cannam | *Chris:* |
91 | 3 | Chris Cannam | |
92 | 3 | Chris Cannam | Another advantage of making processall (or whatever it's called) callable through the module rather than the plugin object is that it |
93 | 3 | Chris Cannam | ensures you don't go and use the plugin for something else afterwards (with unpredictable results). |
94 | 3 | Chris Cannam | |
95 | 3 | Chris Cannam | Trouble is if you're running it on array data, you have to supply the rate somewhere so you must either load() explicitly or provide the rate |
96 | 3 | Chris Cannam | to processall. |
97 | 3 | Chris Cannam | |
98 | 3 | Chris Cannam | (I am very keen to make simple use cases like "give me the beat times in this audio file" as simple as possible. Of course plenty of other APIs |
99 | 3 | Chris Cannam | can now be used to do that, but that doesn't mean we shouldn't do the best job possible with this one) |