Mercurial > code > home > repos > homeauto
annotate service/audioInputLevels/audioInputLevelsPulse.py @ 1112:03b4882517dd
audiolevels output to influxdb
Ignore-this: e5f3559928a638bb311eb70dc751ef87
darcs-hash:e419b63c2335d1dd2a5267440bf73ee9b1bf3291
author | drewp <drewp@bigasterisk.com> |
---|---|
date | Fri, 16 Sep 2016 01:22:11 -0700 |
parents | 64a2ba088665 |
children | 30cd6cb833e3 |
rev | line source |
---|---|
887 | 1 # based on http://freshfoo.com/blog/pulseaudio_monitoring |
2 from __future__ import division | |
1112
03b4882517dd
audiolevels output to influxdb
drewp <drewp@bigasterisk.com>
parents:
887
diff
changeset
|
3 import socket, argparse, time |
887 | 4 from Queue import Queue |
5 from ctypes import POINTER, c_ubyte, c_void_p, c_ulong, cast | |
1112
03b4882517dd
audiolevels output to influxdb
drewp <drewp@bigasterisk.com>
parents:
887
diff
changeset
|
6 from influxdb import InfluxDBClient |
887 | 7 |
8 # From https://github.com/Valodim/python-pulseaudio | |
9 from pulseaudio import lib_pulseaudio as P | |
10 | |
11 METER_RATE = 1 | |
12 MAX_SAMPLE_VALUE = 127 | |
13 | |
14 class PeakMonitor(object): | |
15 | |
16 def __init__(self, source_name, rate): | |
17 self.source_name = source_name | |
18 self.rate = rate | |
19 | |
20 # Wrap callback methods in appropriate ctypefunc instances so | |
21 # that the Pulseaudio C API can call them | |
22 self._context_notify_cb = P.pa_context_notify_cb_t(self.context_notify_cb) | |
23 self._source_info_cb = P.pa_source_info_cb_t(self.source_info_cb) | |
24 self._stream_read_cb = P.pa_stream_request_cb_t(self.stream_read_cb) | |
25 | |
26 # stream_read_cb() puts peak samples into this Queue instance | |
27 self._samples = Queue() | |
28 | |
29 # Create the mainloop thread and set our context_notify_cb | |
30 # method to be called when there's updates relating to the | |
31 # connection to Pulseaudio | |
32 _mainloop = P.pa_threaded_mainloop_new() | |
33 _mainloop_api = P.pa_threaded_mainloop_get_api(_mainloop) | |
34 context = P.pa_context_new(_mainloop_api, 'peak_demo') | |
35 P.pa_context_set_state_callback(context, self._context_notify_cb, None) | |
36 P.pa_context_connect(context, None, 0, None) | |
37 P.pa_threaded_mainloop_start(_mainloop) | |
38 | |
39 def __iter__(self): | |
40 while True: | |
41 yield self._samples.get() | |
42 | |
43 def context_notify_cb(self, context, _): | |
44 state = P.pa_context_get_state(context) | |
45 | |
46 if state == P.PA_CONTEXT_READY: | |
47 print "Pulseaudio connection ready..." | |
48 # Connected to Pulseaudio. Now request that source_info_cb | |
49 # be called with information about the available sources. | |
50 o = P.pa_context_get_source_info_list(context, self._source_info_cb, None) | |
51 P.pa_operation_unref(o) | |
52 | |
53 elif state == P.PA_CONTEXT_FAILED : | |
54 print "Connection failed" | |
55 | |
56 elif state == P.PA_CONTEXT_TERMINATED: | |
57 print "Connection terminated" | |
58 | |
59 def source_info_cb(self, context, source_info_p, _, __): | |
60 if not source_info_p: | |
61 return | |
62 | |
63 source_info = source_info_p.contents | |
64 | |
65 if source_info.name == self.source_name: | |
66 # Found the source we want to monitor for peak levels. | |
67 # Tell PA to call stream_read_cb with peak samples. | |
68 print 'setting up peak recording using', source_info.name | |
69 print 'description:', source_info.description | |
70 samplespec = P.pa_sample_spec() | |
71 samplespec.channels = 1 | |
72 samplespec.format = P.PA_SAMPLE_U8 | |
73 samplespec.rate = self.rate | |
74 | |
75 pa_stream = P.pa_stream_new(context, "peak detect demo", samplespec, None) | |
76 P.pa_stream_set_read_callback(pa_stream, | |
77 self._stream_read_cb, | |
78 source_info.index) | |
79 P.pa_stream_connect_record(pa_stream, | |
80 source_info.name, | |
81 None, | |
82 P.PA_STREAM_PEAK_DETECT) | |
83 | |
84 def stream_read_cb(self, stream, length, index_incr): | |
85 data = c_void_p() | |
86 P.pa_stream_peek(stream, data, c_ulong(length)) | |
87 data = cast(data, POINTER(c_ubyte)) | |
88 for i in xrange(length): | |
89 # When PA_SAMPLE_U8 is used, samples values range from 128 | |
90 # to 255 because the underlying audio data is signed but | |
91 # it doesn't make sense to return signed peaks. | |
92 self._samples.put(data[i] - 128) | |
93 P.pa_stream_drop(stream) | |
94 | |
95 def main(): | |
96 | |
97 | |
98 parser = argparse.ArgumentParser() | |
99 parser.add_argument( | |
100 '--source', required=True, | |
101 help='pulseaudio source name (use `pactl list sources | grep Name`)') | |
102 | |
103 args = parser.parse_args() | |
104 | |
1112
03b4882517dd
audiolevels output to influxdb
drewp <drewp@bigasterisk.com>
parents:
887
diff
changeset
|
105 def ipv6Init(self, host="localhost", port=2003): |
03b4882517dd
audiolevels output to influxdb
drewp <drewp@bigasterisk.com>
parents:
887
diff
changeset
|
106 self._addr = (host, port, 0, 0) |
03b4882517dd
audiolevels output to influxdb
drewp <drewp@bigasterisk.com>
parents:
887
diff
changeset
|
107 self._sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) |
03b4882517dd
audiolevels output to influxdb
drewp <drewp@bigasterisk.com>
parents:
887
diff
changeset
|
108 self._sock.connect(self._addr) |
03b4882517dd
audiolevels output to influxdb
drewp <drewp@bigasterisk.com>
parents:
887
diff
changeset
|
109 |
03b4882517dd
audiolevels output to influxdb
drewp <drewp@bigasterisk.com>
parents:
887
diff
changeset
|
110 influx = InfluxDBClient('bang6', 9060, 'root', 'root', 'main') |
03b4882517dd
audiolevels output to influxdb
drewp <drewp@bigasterisk.com>
parents:
887
diff
changeset
|
111 |
03b4882517dd
audiolevels output to influxdb
drewp <drewp@bigasterisk.com>
parents:
887
diff
changeset
|
112 hostname = socket.gethostname() |
887 | 113 monitor = PeakMonitor(args.source, METER_RATE) |
114 for sample in monitor: | |
115 #print ' %3d %s' % (sample, '>' * sample) | |
1112
03b4882517dd
audiolevels output to influxdb
drewp <drewp@bigasterisk.com>
parents:
887
diff
changeset
|
116 influx.write_points([{'measurement': 'audioLevel', |
03b4882517dd
audiolevels output to influxdb
drewp <drewp@bigasterisk.com>
parents:
887
diff
changeset
|
117 "tags": dict(stat='max', location=hostname), |
03b4882517dd
audiolevels output to influxdb
drewp <drewp@bigasterisk.com>
parents:
887
diff
changeset
|
118 "fields": {"value": sample / 128}, |
03b4882517dd
audiolevels output to influxdb
drewp <drewp@bigasterisk.com>
parents:
887
diff
changeset
|
119 "time": int(time.time())}], time_precision='s') |
887 | 120 |
121 if __name__ == '__main__': | |
122 main() |