82 return 0; |
115 return 0; |
83 } |
116 } |
84 |
117 |
85 qint64 AudioInfo::writeData(const char *data, qint64 len) |
118 qint64 AudioInfo::writeData(const char *data, qint64 len) |
86 { |
119 { |
87 int samples = len/2; // 2 bytes per sample |
120 if (m_maxAmplitude) { |
88 int maxAmp = 32768; // max for S16 samples |
121 Q_ASSERT(m_format.sampleSize() % 8 == 0); |
89 bool clipping = false; |
122 const int channelBytes = m_format.sampleSize() / 8; |
90 |
123 const int sampleBytes = m_format.channels() * channelBytes; |
91 m_maxValue = 0; |
124 Q_ASSERT(len % sampleBytes == 0); |
92 |
125 const int numSamples = len / sampleBytes; |
93 qint16 *s = (qint16*)data; |
126 |
94 |
127 quint16 maxValue = 0; |
95 // sample format is S16LE, only! |
128 const unsigned char *ptr = reinterpret_cast<const unsigned char *>(data); |
96 |
129 |
97 for (int i = 0; i < samples; ++i) { |
130 for (int i = 0; i < numSamples; ++i) { |
98 qint16 sample = *s; |
131 for(int j = 0; j < m_format.channels(); ++j) { |
99 s++; |
132 quint16 value = 0; |
100 if (abs(sample) > m_maxValue) m_maxValue = abs(sample); |
133 |
101 } |
134 if (m_format.sampleSize() == 8 && m_format.sampleType() == QAudioFormat::UnSignedInt) { |
102 // check for clipping |
135 value = *reinterpret_cast<const quint8*>(ptr); |
103 if (m_maxValue >= (maxAmp - 1)) |
136 } else if (m_format.sampleSize() == 8 && m_format.sampleType() == QAudioFormat::SignedInt) { |
104 clipping = true; |
137 value = qAbs(*reinterpret_cast<const qint8*>(ptr)); |
105 |
138 } else if (m_format.sampleSize() == 16 && m_format.sampleType() == QAudioFormat::UnSignedInt) { |
106 float value = ((float)m_maxValue/(float)maxAmp); |
139 if (m_format.byteOrder() == QAudioFormat::LittleEndian) |
107 if (clipping) |
140 value = qFromLittleEndian<quint16>(ptr); |
108 m_maxValue = 100; |
141 else |
109 else |
142 value = qFromBigEndian<quint16>(ptr); |
110 m_maxValue = (int)(value*100); |
143 } else if (m_format.sampleSize() == 16 && m_format.sampleType() == QAudioFormat::SignedInt) { |
|
144 if (m_format.byteOrder() == QAudioFormat::LittleEndian) |
|
145 value = qAbs(qFromLittleEndian<qint16>(ptr)); |
|
146 else |
|
147 value = qAbs(qFromBigEndian<qint16>(ptr)); |
|
148 } |
|
149 |
|
150 maxValue = qMax(value, maxValue); |
|
151 ptr += channelBytes; |
|
152 } |
|
153 } |
|
154 |
|
155 maxValue = qMin(maxValue, m_maxAmplitude); |
|
156 m_level = qreal(maxValue) / m_maxAmplitude; |
|
157 } |
111 |
158 |
112 emit update(); |
159 emit update(); |
113 |
|
114 return len; |
160 return len; |
115 } |
|
116 |
|
117 int AudioInfo::LinearMax() |
|
118 { |
|
119 return m_maxValue; |
|
120 } |
161 } |
121 |
162 |
122 RenderArea::RenderArea(QWidget *parent) |
163 RenderArea::RenderArea(QWidget *parent) |
123 : QWidget(parent) |
164 : QWidget(parent) |
124 { |
165 { |
125 setBackgroundRole(QPalette::Base); |
166 setBackgroundRole(QPalette::Base); |
126 setAutoFillBackground(true); |
167 setAutoFillBackground(true); |
127 |
168 |
128 level = 0; |
169 m_level = 0; |
129 setMinimumHeight(30); |
170 setMinimumHeight(30); |
130 setMinimumWidth(200); |
171 setMinimumWidth(200); |
131 } |
172 } |
132 |
173 |
133 void RenderArea::paintEvent(QPaintEvent * /* event */) |
174 void RenderArea::paintEvent(QPaintEvent * /* event */) |
155 |
196 |
156 painter.drawLine(QPoint(x1, y1),QPoint(x2, y2)); |
197 painter.drawLine(QPoint(x1, y1),QPoint(x2, y2)); |
157 } |
198 } |
158 } |
199 } |
159 |
200 |
160 void RenderArea::setLevel(int value) |
201 void RenderArea::setLevel(qreal value) |
161 { |
202 { |
162 level = value; |
203 m_level = value; |
163 repaint(); |
204 repaint(); |
164 } |
205 } |
165 |
206 |
166 |
207 |
167 InputTest::InputTest() |
208 InputTest::InputTest() |
168 { |
209 : m_canvas(0) |
169 QWidget *window = new QWidget; |
210 , m_modeButton(0) |
170 QVBoxLayout* layout = new QVBoxLayout; |
211 , m_suspendResumeButton(0) |
171 |
212 , m_deviceBox(0) |
172 canvas = new RenderArea; |
213 , m_device(QAudioDeviceInfo::defaultInputDevice()) |
173 layout->addWidget(canvas); |
214 , m_audioInfo(0) |
174 |
215 , m_audioInput(0) |
175 deviceBox = new QComboBox(this); |
216 , m_input(0) |
|
217 , m_pullMode(false) |
|
218 , m_buffer(BufferSize, 0) |
|
219 { |
|
220 initializeWindow(); |
|
221 initializeAudio(); |
|
222 } |
|
223 |
|
224 InputTest::~InputTest() {} |
|
225 |
|
226 void InputTest::initializeWindow() |
|
227 { |
|
228 QScopedPointer<QWidget> window(new QWidget); |
|
229 QScopedPointer<QVBoxLayout> layout(new QVBoxLayout); |
|
230 |
|
231 m_canvas = new RenderArea(this); |
|
232 layout->addWidget(m_canvas); |
|
233 |
|
234 m_deviceBox = new QComboBox(this); |
176 QList<QAudioDeviceInfo> devices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); |
235 QList<QAudioDeviceInfo> devices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); |
177 for(int i = 0; i < devices.size(); ++i) |
236 for(int i = 0; i < devices.size(); ++i) |
178 deviceBox->addItem(devices.at(i).deviceName(), qVariantFromValue(devices.at(i))); |
237 m_deviceBox->addItem(devices.at(i).deviceName(), qVariantFromValue(devices.at(i))); |
179 |
238 |
180 connect(deviceBox, SIGNAL(activated(int)), SLOT(deviceChanged(int))); |
239 connect(m_deviceBox, SIGNAL(activated(int)), SLOT(deviceChanged(int))); |
181 layout->addWidget(deviceBox); |
240 layout->addWidget(m_deviceBox); |
182 |
241 |
183 button = new QPushButton(this); |
242 m_modeButton = new QPushButton(this); |
184 button->setText(tr("Click for Push Mode")); |
243 m_modeButton->setText(PushModeLabel); |
185 connect(button, SIGNAL(clicked()), SLOT(toggleMode())); |
244 connect(m_modeButton, SIGNAL(clicked()), SLOT(toggleMode())); |
186 layout->addWidget(button); |
245 layout->addWidget(m_modeButton); |
187 |
246 |
188 button2 = new QPushButton(this); |
247 m_suspendResumeButton = new QPushButton(this); |
189 button2->setText(tr("Click To Suspend")); |
248 m_suspendResumeButton->setText(SuspendLabel); |
190 connect(button2, SIGNAL(clicked()), SLOT(toggleSuspend())); |
249 connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspend())); |
191 layout->addWidget(button2); |
250 layout->addWidget(m_suspendResumeButton); |
192 |
251 |
193 window->setLayout(layout); |
252 window->setLayout(layout.data()); |
194 setCentralWidget(window); |
253 layout.take(); // ownership transferred |
195 window->show(); |
254 |
196 |
255 setCentralWidget(window.data()); |
197 buffer = new char[BUFFER_SIZE]; |
256 QWidget *const windowPtr = window.take(); // ownership transferred |
198 |
257 windowPtr->show(); |
199 pullMode = true; |
258 } |
200 |
259 |
201 format.setFrequency(8000); |
260 void InputTest::initializeAudio() |
202 format.setChannels(1); |
261 { |
203 format.setSampleSize(16); |
262 m_pullMode = true; |
204 format.setSampleType(QAudioFormat::SignedInt); |
263 |
205 format.setByteOrder(QAudioFormat::LittleEndian); |
264 m_format.setFrequency(8000); |
206 format.setCodec("audio/pcm"); |
265 m_format.setChannels(1); |
|
266 m_format.setSampleSize(16); |
|
267 m_format.setSampleType(QAudioFormat::SignedInt); |
|
268 m_format.setByteOrder(QAudioFormat::LittleEndian); |
|
269 m_format.setCodec("audio/pcm"); |
207 |
270 |
208 QAudioDeviceInfo info(QAudioDeviceInfo::defaultInputDevice()); |
271 QAudioDeviceInfo info(QAudioDeviceInfo::defaultInputDevice()); |
209 if (!info.isFormatSupported(format)) { |
272 if (!info.isFormatSupported(m_format)) { |
210 qWarning()<<"default format not supported try to use nearest"; |
273 qWarning() << "Default format not supported - trying to use nearest"; |
211 format = info.nearestFormat(format); |
274 m_format = info.nearestFormat(m_format); |
212 } |
275 } |
213 |
276 |
214 if(format.sampleSize() != 16) { |
277 m_audioInfo = new AudioInfo(m_format, this); |
215 qWarning()<<"audio device doesn't support 16 bit samples, example cannot run"; |
278 connect(m_audioInfo, SIGNAL(update()), SLOT(refreshDisplay())); |
|
279 |
|
280 createAudioInput(); |
|
281 } |
|
282 |
|
283 void InputTest::createAudioInput() |
|
284 { |
|
285 m_audioInput = new QAudioInput(m_device, m_format, this); |
|
286 connect(m_audioInput, SIGNAL(notify()), SLOT(notified())); |
|
287 connect(m_audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State))); |
|
288 m_audioInfo->start(); |
|
289 m_audioInput->start(m_audioInfo); |
|
290 } |
|
291 |
|
292 void InputTest::notified() |
|
293 { |
|
294 qWarning() << "bytesReady = " << m_audioInput->bytesReady() |
|
295 << ", " << "elapsedUSecs = " <<m_audioInput->elapsedUSecs() |
|
296 << ", " << "processedUSecs = "<<m_audioInput->processedUSecs(); |
|
297 } |
|
298 |
|
299 void InputTest::readMore() |
|
300 { |
|
301 if(!m_audioInput) |
216 return; |
302 return; |
217 } |
303 qint64 len = m_audioInput->bytesReady(); |
218 |
|
219 audioInput = new QAudioInput(format,this); |
|
220 connect(audioInput, SIGNAL(notify()), SLOT(status())); |
|
221 connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(state(QAudio::State))); |
|
222 audioinfo = new AudioInfo(this,audioInput); |
|
223 connect(audioinfo, SIGNAL(update()), SLOT(refreshDisplay())); |
|
224 audioinfo->start(); |
|
225 audioInput->start(audioinfo); |
|
226 } |
|
227 |
|
228 InputTest::~InputTest() {} |
|
229 |
|
230 void InputTest::status() |
|
231 { |
|
232 qWarning()<<"bytesReady = "<<audioInput->bytesReady()<<" bytes, elapsedUSecs = "<<audioInput->elapsedUSecs()<<", processedUSecs = "<<audioInput->processedUSecs(); |
|
233 } |
|
234 |
|
235 void InputTest::readMore() |
|
236 { |
|
237 if(!audioInput) |
|
238 return; |
|
239 qint64 len = audioInput->bytesReady(); |
|
240 if(len > 4096) |
304 if(len > 4096) |
241 len = 4096; |
305 len = 4096; |
242 qint64 l = input->read(buffer,len); |
306 qint64 l = m_input->read(m_buffer.data(), len); |
243 if(l > 0) { |
307 if(l > 0) { |
244 audioinfo->write(buffer,l); |
308 m_audioInfo->write(m_buffer.constData(), l); |
245 } |
309 } |
246 } |
310 } |
247 |
311 |
248 void InputTest::toggleMode() |
312 void InputTest::toggleMode() |
249 { |
313 { |
250 // Change bewteen pull and push modes |
314 // Change bewteen pull and push modes |
251 audioInput->stop(); |
315 m_audioInput->stop(); |
252 |
316 |
253 if (pullMode) { |
317 if (m_pullMode) { |
254 button->setText(tr("Click for Pull Mode")); |
318 m_modeButton->setText(PullModeLabel); |
255 input = audioInput->start(); |
319 m_input = m_audioInput->start(); |
256 connect(input, SIGNAL(readyRead()), SLOT(readMore())); |
320 connect(m_input, SIGNAL(readyRead()), SLOT(readMore())); |
257 pullMode = false; |
321 m_pullMode = false; |
258 } else { |
322 } else { |
259 button->setText(tr("Click for Push Mode")); |
323 m_modeButton->setText(PushModeLabel); |
260 pullMode = true; |
324 m_pullMode = true; |
261 audioInput->start(audioinfo); |
325 m_audioInput->start(m_audioInfo); |
262 } |
326 } |
|
327 |
|
328 m_suspendResumeButton->setText(SuspendLabel); |
263 } |
329 } |
264 |
330 |
265 void InputTest::toggleSuspend() |
331 void InputTest::toggleSuspend() |
266 { |
332 { |
267 // toggle suspend/resume |
333 // toggle suspend/resume |
268 if(audioInput->state() == QAudio::SuspendedState) { |
334 if(m_audioInput->state() == QAudio::SuspendedState) { |
269 qWarning() << "status: Suspended, resume()"; |
335 qWarning() << "status: Suspended, resume()"; |
270 audioInput->resume(); |
336 m_audioInput->resume(); |
271 button2->setText("Click To Suspend"); |
337 m_suspendResumeButton->setText(SuspendLabel); |
272 } else if (audioInput->state() == QAudio::ActiveState) { |
338 } else if (m_audioInput->state() == QAudio::ActiveState) { |
273 qWarning() << "status: Active, suspend()"; |
339 qWarning() << "status: Active, suspend()"; |
274 audioInput->suspend(); |
340 m_audioInput->suspend(); |
275 button2->setText("Click To Resume"); |
341 m_suspendResumeButton->setText(ResumeLabel); |
276 } else if (audioInput->state() == QAudio::StoppedState) { |
342 } else if (m_audioInput->state() == QAudio::StoppedState) { |
277 qWarning() << "status: Stopped, resume()"; |
343 qWarning() << "status: Stopped, resume()"; |
278 audioInput->resume(); |
344 m_audioInput->resume(); |
279 button2->setText("Click To Suspend"); |
345 m_suspendResumeButton->setText(SuspendLabel); |
280 } else if (audioInput->state() == QAudio::IdleState) { |
346 } else if (m_audioInput->state() == QAudio::IdleState) { |
281 qWarning() << "status: IdleState"; |
347 qWarning() << "status: IdleState"; |
282 } |
348 } |
283 } |
349 } |
284 |
350 |
285 void InputTest::state(QAudio::State state) |
351 void InputTest::stateChanged(QAudio::State state) |
286 { |
352 { |
287 qWarning() << " state=" << state; |
353 qWarning() << "state = " << state; |
288 } |
354 } |
289 |
355 |
290 void InputTest::refreshDisplay() |
356 void InputTest::refreshDisplay() |
291 { |
357 { |
292 canvas->setLevel(audioinfo->LinearMax()); |
358 m_canvas->setLevel(m_audioInfo->level()); |
293 canvas->repaint(); |
359 m_canvas->repaint(); |
294 } |
360 } |
295 |
361 |
296 void InputTest::deviceChanged(int idx) |
362 void InputTest::deviceChanged(int index) |
297 { |
363 { |
298 audioinfo->stop(); |
364 m_audioInfo->stop(); |
299 audioInput->stop(); |
365 m_audioInput->stop(); |
300 audioInput->disconnect(this); |
366 m_audioInput->disconnect(this); |
301 delete audioInput; |
367 delete m_audioInput; |
302 |
368 |
303 device = deviceBox->itemData(idx).value<QAudioDeviceInfo>(); |
369 m_device = m_deviceBox->itemData(index).value<QAudioDeviceInfo>(); |
304 audioInput = new QAudioInput(device, format, this); |
370 createAudioInput(); |
305 connect(audioInput, SIGNAL(notify()), SLOT(status())); |
371 } |
306 connect(audioInput, SIGNAL(stateChanged(QAudio::State)), SLOT(state(QAudio::State))); |
|
307 audioinfo->start(); |
|
308 audioInput->start(audioinfo); |
|
309 } |
|