~mterry/+junk/u8.2

« back to all changes in this revision

Viewing changes to plugins/HudClient/volumepeakdetector.cpp

  • Committer: Michael Terry
  • Date: 2014-11-17 14:56:04 UTC
  • mfrom: (1317.1.118 unity8)
  • Revision ID: michael.terry@canonical.com-20141117145604-96dn9p5nwkifq2f4
MergeĀ fromĀ trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
 */
16
 
 
17
 
#include "volumepeakdetector.h"
18
 
 
19
 
static void stream_read_callback(pa_stream * /*s*/, size_t /*length*/, void *userdata) {
20
 
    PulseAudioVolumePeakDetector *peakDetector = static_cast<PulseAudioVolumePeakDetector *>(userdata);
21
 
    peakDetector->processData();
22
 
}
23
 
 
24
 
static void context_state_callback(pa_context *c, void *userdata) {
25
 
    PulseAudioVolumePeakDetector *peakDetector = static_cast<PulseAudioVolumePeakDetector *>(userdata);
26
 
 
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;
32
 
 
33
 
        case PA_CONTEXT_READY:
34
 
            peakDetector->startStream();
35
 
            break;
36
 
 
37
 
        case PA_CONTEXT_TERMINATED:
38
 
        case PA_CONTEXT_FAILED:
39
 
        default:
40
 
            peakDetector->quit();
41
 
    }
42
 
}
43
 
 
44
 
PulseAudioVolumePeakDetector::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)
51
 
{
52
 
}
53
 
 
54
 
void PulseAudioVolumePeakDetector::start()
55
 
{
56
 
    pa_mainloop *mainloop = nullptr;
57
 
 
58
 
    /* Set up a new main loop */
59
 
    mainloop = pa_mainloop_new();
60
 
    if (!mainloop)
61
 
        return;
62
 
 
63
 
    m_mainloop_api = pa_mainloop_get_api(mainloop);
64
 
 
65
 
    /* Create a new connection context */
66
 
    m_context = pa_context_new(m_mainloop_api, nullptr);
67
 
    if (!m_context) {
68
 
        goto quit;
69
 
    }
70
 
 
71
 
    pa_context_set_state_callback(m_context, context_state_callback, this);
72
 
 
73
 
    /* Connect the context */
74
 
    if (pa_context_connect(m_context, nullptr, PA_CONTEXT_NOFLAGS, nullptr) < 0) {
75
 
        goto quit;
76
 
    }
77
 
 
78
 
    /* Run the main loop */
79
 
    if (pa_mainloop_run(mainloop, nullptr) < 0) {
80
 
        goto quit;
81
 
    }
82
 
 
83
 
quit:
84
 
    if (m_stream)
85
 
        pa_stream_unref(m_stream);
86
 
 
87
 
    if (m_context)
88
 
        pa_context_unref(m_context);
89
 
 
90
 
    if (mainloop) {
91
 
        pa_signal_done();
92
 
        pa_mainloop_free(mainloop);
93
 
    }
94
 
 
95
 
    QThread::currentThread()->quit();
96
 
}
97
 
 
98
 
int PulseAudioVolumePeakDetector::nAccumulatedValuesLimit() const
99
 
{
100
 
    return m_accumulatedValuesLimit;
101
 
}
102
 
 
103
 
void PulseAudioVolumePeakDetector::setNAccumulatedValuesLimit(int limit)
104
 
{
105
 
    m_accumulatedValuesLimit = limit;
106
 
}
107
 
 
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;
113
 
 
114
 
void PulseAudioVolumePeakDetector::startStream()
115
 
{
116
 
    pa_buffer_attr buffer_attr;
117
 
    pa_proplist *proplist = pa_proplist_new();
118
 
 
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;
127
 
 
128
 
    pa_channel_map channel_map;
129
 
 
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");
132
 
 
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);
140
 
 
141
 
    pa_stream_set_read_callback(m_stream, stream_read_callback, this);
142
 
 
143
 
    memset(&buffer_attr, 0, sizeof(buffer_attr));
144
 
    buffer_attr.maxlength = (uint32_t) -1;
145
 
    buffer_attr.fragsize = sizeof(float);
146
 
 
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
 
    }
150
 
}
151
 
 
152
 
void PulseAudioVolumePeakDetector::processData()
153
 
{
154
 
    while (pa_stream_readable_size(m_stream) > 0) {
155
 
        const void *data;
156
 
        size_t length;
157
 
 
158
 
        if (pa_stream_peek(m_stream, &data, &length) < 0) {
159
 
            quit();
160
 
            return;
161
 
        }
162
 
 
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
 
        }
176
 
 
177
 
        pa_stream_drop(m_stream);
178
 
    }
179
 
}
180
 
 
181
 
void PulseAudioVolumePeakDetector::quit()
182
 
{
183
 
    m_mainloop_api->quit(m_mainloop_api, 0);
184
 
}
185
 
 
186
 
 
187
 
/* VolumePeakDetector */
188
 
 
189
 
VolumePeakDetector::VolumePeakDetector()
190
 
{
191
 
    QObject::connect(&m_thread, SIGNAL(started()), &m_volumeDetector, SLOT(start()));
192
 
    QObject::connect(&m_volumeDetector, SIGNAL(newPeak(float)), this, SIGNAL(newPeak(float)));
193
 
 
194
 
    m_volumeDetector.moveToThread(&m_thread);
195
 
}
196
 
 
197
 
bool VolumePeakDetector::enabled() const
198
 
{
199
 
    return m_thread.isRunning();
200
 
}
201
 
 
202
 
int VolumePeakDetector::desiredInterval() const
203
 
{
204
 
    return m_volumeDetector.nAccumulatedValuesLimit() * 1000 / voice_needed_rate;
205
 
}
206
 
 
207
 
void VolumePeakDetector::setDesiredInterval(int interval)
208
 
{
209
 
    m_volumeDetector.setNAccumulatedValuesLimit(voice_needed_rate * interval / 1000);
210
 
}
211
 
 
212
 
void VolumePeakDetector::setEnabled(int enabled)
213
 
{
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
 
    }
223
 
}