~mhr3/unity8/fix-1297246

« back to all changes in this revision

Viewing changes to plugins/HudClient/volumepeakdetector.cpp

  • Committer: Michał Sawicz
  • Date: 2013-06-05 22:03:08 UTC
  • Revision ID: michal.sawicz@canonical.com-20130605220308-yny8fv3futtr04fg
Inital unity8 commit.

Previous history can be found at https://code.launchpad.net/~unity-team/unity/phablet

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(NULL)
 
49
 , m_mainloop_api(NULL)
 
50
 , m_stream(NULL)
 
51
{
 
52
}
 
53
 
 
54
void PulseAudioVolumePeakDetector::start()
 
55
{
 
56
    pa_mainloop *mainloop = NULL;
 
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, NULL);
 
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, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0) {
 
75
        goto quit;
 
76
    }
 
77
 
 
78
    /* Run the main loop */
 
79
    if (pa_mainloop_run(mainloop, NULL) < 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, NULL, &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, NULL, &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
}