Unity 8
 All Classes Functions Properties
volumepeakdetector.cpp
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 }