annotate service/audioInputLevels/audioInputLevelsPulse.py @ 82:d288bc1174d4

audio input levels to graphite Ignore-this: b1422609f3bf51778b8b2284ca914916
author drewp@bigasterisk.com
date Fri, 02 Aug 2013 07:55:45 -0700
parents
children f84129fbbe38
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
82
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
1 # based on http://freshfoo.com/blog/pulseaudio_monitoring
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
2 from __future__ import division
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
3 import socket, argparse
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
4 from Queue import Queue
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
5 from ctypes import POINTER, c_ubyte, c_void_p, c_ulong, cast
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
6 import galena
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
7
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
8 # From https://github.com/Valodim/python-pulseaudio
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
9 from pulseaudio import lib_pulseaudio as P
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
10
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
11 METER_RATE = 1
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
12 MAX_SAMPLE_VALUE = 127
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
13
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
14 class PeakMonitor(object):
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
15
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
16 def __init__(self, source_name, rate):
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
17 self.source_name = source_name
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
18 self.rate = rate
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
19
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
20 # Wrap callback methods in appropriate ctypefunc instances so
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
21 # that the Pulseaudio C API can call them
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
22 self._context_notify_cb = P.pa_context_notify_cb_t(self.context_notify_cb)
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
23 self._source_info_cb = P.pa_source_info_cb_t(self.source_info_cb)
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
24 self._stream_read_cb = P.pa_stream_request_cb_t(self.stream_read_cb)
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
25
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
26 # stream_read_cb() puts peak samples into this Queue instance
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
27 self._samples = Queue()
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
28
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
29 # Create the mainloop thread and set our context_notify_cb
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
30 # method to be called when there's updates relating to the
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
31 # connection to Pulseaudio
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
32 _mainloop = P.pa_threaded_mainloop_new()
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
33 _mainloop_api = P.pa_threaded_mainloop_get_api(_mainloop)
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
34 context = P.pa_context_new(_mainloop_api, 'peak_demo')
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
35 P.pa_context_set_state_callback(context, self._context_notify_cb, None)
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
36 P.pa_context_connect(context, None, 0, None)
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
37 P.pa_threaded_mainloop_start(_mainloop)
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
38
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
39 def __iter__(self):
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
40 while True:
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
41 yield self._samples.get()
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
42
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
43 def context_notify_cb(self, context, _):
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
44 state = P.pa_context_get_state(context)
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
45
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
46 if state == P.PA_CONTEXT_READY:
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
47 print "Pulseaudio connection ready..."
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
48 # Connected to Pulseaudio. Now request that source_info_cb
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
49 # be called with information about the available sources.
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
50 o = P.pa_context_get_source_info_list(context, self._source_info_cb, None)
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
51 P.pa_operation_unref(o)
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
52
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
53 elif state == P.PA_CONTEXT_FAILED :
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
54 print "Connection failed"
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
55
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
56 elif state == P.PA_CONTEXT_TERMINATED:
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
57 print "Connection terminated"
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
58
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
59 def source_info_cb(self, context, source_info_p, _, __):
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
60 if not source_info_p:
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
61 return
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
62
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
63 source_info = source_info_p.contents
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
64
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
65 if source_info.name == self.source_name:
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
66 # Found the source we want to monitor for peak levels.
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
67 # Tell PA to call stream_read_cb with peak samples.
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
68 print 'setting up peak recording using', source_info.name
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
69 print 'description:', source_info.description
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
70 samplespec = P.pa_sample_spec()
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
71 samplespec.channels = 1
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
72 samplespec.format = P.PA_SAMPLE_U8
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
73 samplespec.rate = self.rate
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
74
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
75 pa_stream = P.pa_stream_new(context, "peak detect demo", samplespec, None)
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
76 P.pa_stream_set_read_callback(pa_stream,
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
77 self._stream_read_cb,
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
78 source_info.index)
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
79 P.pa_stream_connect_record(pa_stream,
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
80 source_info.name,
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
81 None,
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
82 P.PA_STREAM_PEAK_DETECT)
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
83
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
84 def stream_read_cb(self, stream, length, index_incr):
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
85 data = c_void_p()
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
86 P.pa_stream_peek(stream, data, c_ulong(length))
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
87 data = cast(data, POINTER(c_ubyte))
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
88 for i in xrange(length):
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
89 # When PA_SAMPLE_U8 is used, samples values range from 128
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
90 # to 255 because the underlying audio data is signed but
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
91 # it doesn't make sense to return signed peaks.
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
92 self._samples.put(data[i] - 128)
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
93 P.pa_stream_drop(stream)
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
94
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
95 def main():
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
96
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
97
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
98 parser = argparse.ArgumentParser()
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
99 parser.add_argument(
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
100 '--source', required=True,
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
101 help='pulseaudio source name (use `pactl list sources | grep Name`)')
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
102
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
103 args = parser.parse_args()
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
104
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
105
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
106 out = galena.Galena(host='bang')
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
107 prefix = 'system.house.audio.%s' % socket.gethostname()
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
108 monitor = PeakMonitor(args.source, METER_RATE)
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
109 for sample in monitor:
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
110 #print ' %3d %s' % (sample, '>' * sample)
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
111 out.send(prefix + ".max", sample / 128)
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
112
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
113 if __name__ == '__main__':
d288bc1174d4 audio input levels to graphite
drewp@bigasterisk.com
parents:
diff changeset
114 main()