2
* Copyright (C) 2012, 2013 Canonical, Ltd.
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.
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.
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/>.
17
#include "volumepeakdetector.h"
19
static void stream_read_callback(pa_stream * /*s*/, size_t /*length*/, void *userdata) {
20
PulseAudioVolumePeakDetector *peakDetector = static_cast<PulseAudioVolumePeakDetector *>(userdata);
21
peakDetector->processData();
24
static 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:
33
case PA_CONTEXT_READY:
34
peakDetector->startStream();
37
case PA_CONTEXT_TERMINATED:
38
case PA_CONTEXT_FAILED:
44
PulseAudioVolumePeakDetector::PulseAudioVolumePeakDetector()
45
: m_accumulatedValue(0)
46
, m_nAccumulatedValues(0)
47
, m_accumulatedValuesLimit(1)
49
, m_mainloop_api(NULL)
54
void PulseAudioVolumePeakDetector::start()
56
pa_mainloop *mainloop = NULL;
58
/* Set up a new main loop */
59
mainloop = pa_mainloop_new();
63
m_mainloop_api = pa_mainloop_get_api(mainloop);
65
/* Create a new connection context */
66
m_context = pa_context_new(m_mainloop_api, NULL);
71
pa_context_set_state_callback(m_context, context_state_callback, this);
73
/* Connect the context */
74
if (pa_context_connect(m_context, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0) {
78
/* Run the main loop */
79
if (pa_mainloop_run(mainloop, NULL) < 0) {
85
pa_stream_unref(m_stream);
88
pa_context_unref(m_context);
92
pa_mainloop_free(mainloop);
95
QThread::currentThread()->quit();
98
int PulseAudioVolumePeakDetector::nAccumulatedValuesLimit() const
100
return m_accumulatedValuesLimit;
103
void 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
112
static uint voice_needed_rate = 16000;
114
void 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, NULL, &sample_spec, &channel_map, proplist);
135
pa_proplist_free(proplist);
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, NULL, &buffer_attr, (pa_stream_flags_t)(PA_STREAM_PEAK_DETECT | PA_STREAM_ADJUST_LATENCY)) < 0) {
152
void PulseAudioVolumePeakDetector::processData()
154
while (pa_stream_readable_size(m_stream) > 0) {
158
if (pa_stream_peek(m_stream, &data, &length) < 0) {
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;
177
pa_stream_drop(m_stream);
181
void PulseAudioVolumePeakDetector::quit()
183
m_mainloop_api->quit(m_mainloop_api, 0);
187
/* VolumePeakDetector */
189
VolumePeakDetector::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);
197
bool VolumePeakDetector::enabled() const
199
return m_thread.isRunning();
202
int VolumePeakDetector::desiredInterval() const
204
return m_volumeDetector.nAccumulatedValuesLimit() * 1000 / voice_needed_rate;
207
void VolumePeakDetector::setDesiredInterval(int interval)
209
m_volumeDetector.setNAccumulatedValuesLimit(voice_needed_rate * interval / 1000);
212
void VolumePeakDetector::setEnabled(int enabled)
215
if (!m_thread.isRunning()) {
219
if (m_thread.isRunning()) {
220
m_volumeDetector.quit();