on bcacecf help sign in
unity8/plugins/HudClient/volumepeakdetector.cpp
223 lines 6.2 kB history raw
1/*
2 * Copyright (C) 2012, 2013 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
17#include "volumepeakdetector.h"
19static void stream_read_callback(pa_stream * /*s*/, size_t /*length*/, void *userdata) {
20 PulseAudioVolumePeakDetector *peakDetector = static_cast<PulseAudioVolumePeakDetector *>(userdata);
21 peakDetector->processData();
24static void context_state_callback(pa_context *c, void *userdata) {
25 PulseAudioVolumePeakDetector *peakDetector = static_cast<PulseAudioVolumePeakDetector *>(userdata);
27 switch (pa_context_get_state(c)) {
28 case PA_CONTEXT_CONNECTING:
29 case PA_CONTEXT_AUTHORIZING:
30 case PA_CONTEXT_SETTING_NAME:
31 break;
33 case PA_CONTEXT_READY:
34 peakDetector->startStream();
35 break;
37 case PA_CONTEXT_TERMINATED:
38 case PA_CONTEXT_FAILED:
39 default:
40 peakDetector->quit();
41 }
44PulseAudioVolumePeakDetector::PulseAudioVolumePeakDetector()
45 : m_accumulatedValue(0)
46 , m_nAccumulatedValues(0)
47 , m_accumulatedValuesLimit(1)
48 , m_context(nullptr)
49 , m_mainloop_api(nullptr)
50 , m_stream(nullptr)
54void PulseAudioVolumePeakDetector::start()
56 pa_mainloop *mainloop = nullptr;
58 /* Set up a new main loop */
59 mainloop = pa_mainloop_new();
60 if (!mainloop)
61 return;
63 m_mainloop_api = pa_mainloop_get_api(mainloop);
65 /* Create a new connection context */
66 m_context = pa_context_new(m_mainloop_api, nullptr);
67 if (!m_context) {
68 goto quit;
69 }
71 pa_context_set_state_callback(m_context, context_state_callback, this);
73 /* Connect the context */
74 if (pa_context_connect(m_context, nullptr, PA_CONTEXT_NOFLAGS, nullptr) < 0) {
75 goto quit;
76 }
78 /* Run the main loop */
79 if (pa_mainloop_run(mainloop, nullptr) < 0) {
80 goto quit;
81 }
83quit:
84 if (m_stream)
85 pa_stream_unref(m_stream);
87 if (m_context)
88 pa_context_unref(m_context);
90 if (mainloop) {
91 pa_signal_done();
92 pa_mainloop_free(mainloop);
93 }
95 QThread::currentThread()->quit();
98int PulseAudioVolumePeakDetector::nAccumulatedValuesLimit() const
100 return m_accumulatedValuesLimit;
103void PulseAudioVolumePeakDetector::setNAccumulatedValuesLimit(int limit)
105 m_accumulatedValuesLimit = limit;
108// FIXME 16000 is hardcoded because julius needs it
109// and the hardware/driver/pulse something gets confused
110// if we use different rates
111// We would be happier with a much smaller value like 100
112static uint voice_needed_rate = 16000;
114void PulseAudioVolumePeakDetector::startStream()
116 pa_buffer_attr buffer_attr;
117 pa_proplist *proplist = pa_proplist_new();
119 // FIXME 16000 is hardcoded because julius needs it
120 // and the hardware/driver/pulse something gets confused
121 // if we use different rates
122 // We would be happier with a much smaller value like 100
123 pa_sample_spec sample_spec;
124 sample_spec.format = PA_SAMPLE_FLOAT32;
125 sample_spec.rate = 16000;
126 sample_spec.channels = 1;
128 pa_channel_map channel_map;
130 pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
131 pa_proplist_sets(proplist, PA_PROP_MEDIA_NAME, "HUD Peak Detector");
133 m_stream = pa_stream_new_with_proplist(m_context, nullptr, &sample_spec, &channel_map, proplist);
134 if (!m_stream) {
135 pa_proplist_free(proplist);
136 quit();
137 return;
138 }
139 pa_proplist_free(proplist);
141 pa_stream_set_read_callback(m_stream, stream_read_callback, this);
143 memset(&buffer_attr, 0, sizeof(buffer_attr));
144 buffer_attr.maxlength = (uint32_t) -1;
145 buffer_attr.fragsize = sizeof(float);
147 if (pa_stream_connect_record(m_stream, nullptr, &buffer_attr, (pa_stream_flags_t)(PA_STREAM_PEAK_DETECT | PA_STREAM_ADJUST_LATENCY)) < 0) {
148 quit();
149 }
152void PulseAudioVolumePeakDetector::processData()
154 while (pa_stream_readable_size(m_stream) > 0) {
155 const void *data;
156 size_t length;
158 if (pa_stream_peek(m_stream, &data, &length) < 0) {
159 quit();
160 return;
161 }
163 const float *values = (float*)data;
164 for (size_t i = 0; i < length / sizeof(float); ++i) {
165 float value = values[i];
166 if (value < 0) value = 0;
167 if (value > 1) value = 1;
168 m_nAccumulatedValues++;
169 m_accumulatedValue += value;
170 if (m_nAccumulatedValues == m_accumulatedValuesLimit) {
171 Q_EMIT newPeak(m_accumulatedValue / m_nAccumulatedValues);
172 m_nAccumulatedValues = 0;
173 m_accumulatedValue = 0;
174 }
175 }
177 pa_stream_drop(m_stream);
178 }
181void PulseAudioVolumePeakDetector::quit()
183 m_mainloop_api->quit(m_mainloop_api, 0);
187/* VolumePeakDetector */
189VolumePeakDetector::VolumePeakDetector()
191 QObject::connect(&m_thread, SIGNAL(started()), &m_volumeDetector, SLOT(start()));
192 QObject::connect(&m_volumeDetector, SIGNAL(newPeak(float)), this, SIGNAL(newPeak(float)));
194 m_volumeDetector.moveToThread(&m_thread);
197bool VolumePeakDetector::enabled() const
199 return m_thread.isRunning();
202int VolumePeakDetector::desiredInterval() const
204 return m_volumeDetector.nAccumulatedValuesLimit() * 1000 / voice_needed_rate;
207void VolumePeakDetector::setDesiredInterval(int interval)
209 m_volumeDetector.setNAccumulatedValuesLimit(voice_needed_rate * interval / 1000);
212void VolumePeakDetector::setEnabled(int enabled)
214 if (enabled) {
215 if (!m_thread.isRunning()) {
216 m_thread.start();
217 }
218 } else {
219 if (m_thread.isRunning()) {
220 m_volumeDetector.quit();
221 }
222 }