[1c190d5] | 1 | #! /usr/bin/python |
---|
| 2 | |
---|
| 3 | import pygst |
---|
| 4 | pygst.require('0.10') |
---|
| 5 | import gst |
---|
| 6 | import gobject |
---|
| 7 | gobject.threads_init () |
---|
| 8 | |
---|
| 9 | def gst_buffer_to_numpy_array(buffer, chan): |
---|
| 10 | import numpy |
---|
| 11 | samples = numpy.frombuffer(buffer.data, dtype=numpy.float32) |
---|
[228e8d8] | 12 | if chan == 1: |
---|
| 13 | return samples.T |
---|
| 14 | else: |
---|
| 15 | samples.resize([len(samples)/chan, chan]) |
---|
| 16 | return samples.T |
---|
[1c190d5] | 17 | |
---|
| 18 | class AubioSink(gst.BaseSink): |
---|
| 19 | _caps = gst.caps_from_string('audio/x-raw-float, \ |
---|
| 20 | rate=[ 1, 2147483647 ], \ |
---|
| 21 | channels=[ 1, 2147483647 ], \ |
---|
| 22 | endianness={ 1234, 4321 }, \ |
---|
| 23 | width=32') |
---|
| 24 | |
---|
| 25 | __gsttemplates__ = ( |
---|
| 26 | gst.PadTemplate ("sink", |
---|
| 27 | gst.PAD_SINK, |
---|
| 28 | gst.PAD_ALWAYS, |
---|
| 29 | _caps), |
---|
| 30 | ) |
---|
| 31 | |
---|
| 32 | def __init__(self, name, process): |
---|
| 33 | self.__gobject_init__() |
---|
| 34 | self.set_name(name) |
---|
| 35 | self.process = process |
---|
| 36 | self.adapter = gst.Adapter() |
---|
| 37 | self.set_property('sync', False) |
---|
| 38 | self.pos = 0 |
---|
| 39 | |
---|
| 40 | def set_property(self, name, value): |
---|
| 41 | if name == 'hopsize': |
---|
| 42 | # blocksize is in byte, convert from hopsize |
---|
| 43 | from struct import calcsize |
---|
| 44 | self.set_property('blocksize', value * calcsize('f')) |
---|
| 45 | else: |
---|
| 46 | super(gst.BaseSink, self).set_property(name, value) |
---|
| 47 | |
---|
| 48 | def do_render(self, buffer): |
---|
| 49 | blocksize = self.get_property('blocksize') |
---|
| 50 | caps = buffer.get_caps() |
---|
| 51 | chan = caps[0]['channels'] |
---|
| 52 | self.adapter.push(buffer) |
---|
| 53 | while self.adapter.available() >= blocksize: |
---|
| 54 | block = self.adapter.take_buffer(blocksize) |
---|
| 55 | v = gst_buffer_to_numpy_array(block, chan) |
---|
| 56 | if self.process: |
---|
| 57 | self.process(v, self.pos) |
---|
[228e8d8] | 58 | self.pos += 1 |
---|
| 59 | remaining = self.adapter.available() |
---|
| 60 | if remaining < blocksize and remaining > 0: |
---|
| 61 | block = self.adapter.take_buffer(remaining) |
---|
| 62 | v = gst_buffer_to_numpy_array(block, chan) |
---|
| 63 | if self.process: |
---|
| 64 | self.process(v, self.pos) |
---|
| 65 | self.pos += 1 |
---|
[1c190d5] | 66 | return gst.FLOW_OK |
---|
| 67 | |
---|
| 68 | gobject.type_register(AubioSink) |
---|
| 69 | |
---|
[1f8e522] | 70 | class aubioinput(gst.Bin): |
---|
| 71 | |
---|
| 72 | ret = 0 |
---|
| 73 | |
---|
| 74 | def __init__(self, uri, process = None, hopsize = 512, |
---|
[1c190d5] | 75 | caps = None): |
---|
[1f8e522] | 76 | if uri.startswith('/'): |
---|
| 77 | from urllib import quote |
---|
| 78 | uri = 'file://'+quote(uri) |
---|
| 79 | src = gst.element_factory_make('uridecodebin') |
---|
| 80 | src.set_property('uri', uri) |
---|
| 81 | src.connect('pad-added', self.source_pad_added_cb) |
---|
[1c190d5] | 82 | conv = gst.element_factory_make('audioconvert') |
---|
[1f8e522] | 83 | self.conv = conv |
---|
[1c190d5] | 84 | rsmpl = gst.element_factory_make('audioresample') |
---|
| 85 | capsfilter = gst.element_factory_make('capsfilter') |
---|
| 86 | if caps: |
---|
| 87 | capsfilter.set_property('caps', gst.caps_from_string(caps)) |
---|
| 88 | sink = AubioSink("AubioSink", process = process) |
---|
| 89 | sink.set_property('hopsize', hopsize) # * calcsize('f')) |
---|
| 90 | |
---|
| 91 | self.pipeline = gst.Pipeline() |
---|
| 92 | |
---|
| 93 | self.bus = self.pipeline.get_bus() |
---|
| 94 | self.bus.add_signal_watch() |
---|
[1f8e522] | 95 | self.bus.connect('message', self.on_eos) |
---|
[1c190d5] | 96 | |
---|
| 97 | self.apad = conv.get_pad('sink') |
---|
| 98 | |
---|
[1f8e522] | 99 | self.pipeline.add(src, conv, rsmpl, capsfilter, sink) |
---|
[1c190d5] | 100 | |
---|
| 101 | gst.element_link_many(conv, rsmpl, capsfilter, sink) |
---|
| 102 | |
---|
| 103 | self.mainloop = gobject.MainLoop() |
---|
| 104 | self.pipeline.set_state(gst.STATE_PLAYING) |
---|
[1f8e522] | 105 | |
---|
| 106 | def run(self): |
---|
[1c190d5] | 107 | self.mainloop.run() |
---|
[1f8e522] | 108 | return self.ret |
---|
[1c190d5] | 109 | |
---|
[1f8e522] | 110 | def source_pad_added_cb(self, src, pad): |
---|
| 111 | name = pad.get_caps()[0].get_name() |
---|
[1c190d5] | 112 | if name == 'audio/x-raw-float' or name == 'audio/x-raw-int': |
---|
[1f8e522] | 113 | pad.link(self.conv.get_pad("sink")) |
---|
| 114 | |
---|
| 115 | def source_pad_removed_cb(self, src, pad): |
---|
| 116 | pad.unlink(self.conv.get_pad("sink")) |
---|
[1c190d5] | 117 | |
---|
| 118 | def on_eos(self, bus, msg): |
---|
[1f8e522] | 119 | if msg.type == gst.MESSAGE_EOS: |
---|
| 120 | self.bus.remove_signal_watch() |
---|
| 121 | self.pipeline.set_state(gst.STATE_PAUSED) |
---|
| 122 | self.mainloop.quit() |
---|
| 123 | elif msg.type == gst.MESSAGE_ERROR: |
---|
| 124 | print "ERROR", msg.parse_error() |
---|
| 125 | self.bus.remove_signal_watch() |
---|
| 126 | self.pipeline.set_state(gst.STATE_PAUSED) |
---|
| 127 | self.mainloop.quit() |
---|
| 128 | self.ret = 1 # set return value to 1 in case of error |
---|
[1c190d5] | 129 | |
---|
| 130 | if __name__ == '__main__': |
---|
| 131 | import sys |
---|
| 132 | if len(sys.argv) < 2: |
---|
| 133 | print "Usage: %s <filename>" % sys.argv[0] |
---|
| 134 | sys.exit(1) |
---|
| 135 | for filename in sys.argv[1:]: |
---|
[1f8e522] | 136 | peak = [0.] # use a mutable |
---|
[1c190d5] | 137 | def process(buf, hop): |
---|
| 138 | peak[0] = max( peak[0], abs(buf.max()) ) |
---|
[1f8e522] | 139 | a = aubioinput(filename, process = process, hopsize = 512) |
---|
| 140 | if a.run() == 0: # only display the results if no |
---|
| 141 | print "Finished reading %s, peak value is %f" % (filename, max(peak)) |
---|