source: trunk/src/multimedia/audio/qaudiooutput_alsa_p.cpp@ 846

Last change on this file since 846 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

  • Property svn:eol-style set to native
File size: 23.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtMultimedia module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42//
43// W A R N I N G
44// -------------
45//
46// This file is not part of the Qt API. It exists for the convenience
47// of other Qt classes. This header file may change from version to
48// version without notice, or even be removed.
49//
50// We mean it.
51//
52
53#include <QtCore/qcoreapplication.h>
54#include "qaudiooutput_alsa_p.h"
55#include "qaudiodeviceinfo_alsa_p.h"
56
57QT_BEGIN_NAMESPACE
58
59//#define DEBUG_AUDIO 1
60
61QAudioOutputPrivate::QAudioOutputPrivate(const QByteArray &device, const QAudioFormat& audioFormat):
62 settings(audioFormat)
63{
64 bytesAvailable = 0;
65 handle = 0;
66 ahandler = 0;
67 access = SND_PCM_ACCESS_RW_INTERLEAVED;
68 pcmformat = SND_PCM_FORMAT_S16;
69 buffer_frames = 0;
70 period_frames = 0;
71 buffer_size = 0;
72 period_size = 0;
73 buffer_time = 100000;
74 period_time = 20000;
75 totalTimeValue = 0;
76 intervalTime = 1000;
77 audioBuffer = 0;
78 errorState = QAudio::NoError;
79 deviceState = QAudio::StoppedState;
80 audioSource = 0;
81 pullMode = true;
82 resuming = false;
83 opened = false;
84
85 m_device = device;
86
87 timer = new QTimer(this);
88 connect(timer,SIGNAL(timeout()),SLOT(userFeed()));
89}
90
91QAudioOutputPrivate::~QAudioOutputPrivate()
92{
93 close();
94 disconnect(timer, SIGNAL(timeout()));
95 QCoreApplication::processEvents();
96 delete timer;
97}
98
99QAudio::Error QAudioOutputPrivate::error() const
100{
101 return errorState;
102}
103
104QAudio::State QAudioOutputPrivate::state() const
105{
106 return deviceState;
107}
108
109void QAudioOutputPrivate::async_callback(snd_async_handler_t *ahandler)
110{
111 QAudioOutputPrivate* audioOut;
112
113 audioOut = static_cast<QAudioOutputPrivate*>
114 (snd_async_handler_get_callback_private(ahandler));
115
116 if((audioOut->deviceState==QAudio::ActiveState)||(audioOut->resuming))
117 audioOut->feedback();
118}
119
120int QAudioOutputPrivate::xrun_recovery(int err)
121{
122 int count = 0;
123 bool reset = false;
124
125 if(err == -EPIPE) {
126 errorState = QAudio::UnderrunError;
127 err = snd_pcm_prepare(handle);
128 if(err < 0)
129 reset = true;
130
131 } else if((err == -ESTRPIPE)||(err == -EIO)) {
132 errorState = QAudio::IOError;
133 while((err = snd_pcm_resume(handle)) == -EAGAIN){
134 usleep(100);
135 count++;
136 if(count > 5) {
137 reset = true;
138 break;
139 }
140 }
141 if(err < 0) {
142 err = snd_pcm_prepare(handle);
143 if(err < 0)
144 reset = true;
145 }
146 }
147 if(reset) {
148 close();
149 open();
150 snd_pcm_prepare(handle);
151 return 0;
152 }
153 return err;
154}
155
156int QAudioOutputPrivate::setFormat()
157{
158 snd_pcm_format_t pcmformat = SND_PCM_FORMAT_UNKNOWN;
159
160 if(settings.sampleSize() == 8) {
161 pcmformat = SND_PCM_FORMAT_U8;
162
163 } else if(settings.sampleSize() == 16) {
164 if(settings.sampleType() == QAudioFormat::SignedInt) {
165 if(settings.byteOrder() == QAudioFormat::LittleEndian)
166 pcmformat = SND_PCM_FORMAT_S16_LE;
167 else
168 pcmformat = SND_PCM_FORMAT_S16_BE;
169 } else if(settings.sampleType() == QAudioFormat::UnSignedInt) {
170 if(settings.byteOrder() == QAudioFormat::LittleEndian)
171 pcmformat = SND_PCM_FORMAT_U16_LE;
172 else
173 pcmformat = SND_PCM_FORMAT_U16_BE;
174 }
175 } else if(settings.sampleSize() == 24) {
176 if(settings.sampleType() == QAudioFormat::SignedInt) {
177 if(settings.byteOrder() == QAudioFormat::LittleEndian)
178 pcmformat = SND_PCM_FORMAT_S24_LE;
179 else
180 pcmformat = SND_PCM_FORMAT_S24_BE;
181 } else if(settings.sampleType() == QAudioFormat::UnSignedInt) {
182 if(settings.byteOrder() == QAudioFormat::LittleEndian)
183 pcmformat = SND_PCM_FORMAT_U24_LE;
184 else
185 pcmformat = SND_PCM_FORMAT_U24_BE;
186 }
187 } else if(settings.sampleSize() == 32) {
188 if(settings.sampleType() == QAudioFormat::SignedInt) {
189 if(settings.byteOrder() == QAudioFormat::LittleEndian)
190 pcmformat = SND_PCM_FORMAT_S32_LE;
191 else
192 pcmformat = SND_PCM_FORMAT_S32_BE;
193 } else if(settings.sampleType() == QAudioFormat::UnSignedInt) {
194 if(settings.byteOrder() == QAudioFormat::LittleEndian)
195 pcmformat = SND_PCM_FORMAT_U32_LE;
196 else
197 pcmformat = SND_PCM_FORMAT_U32_BE;
198 } else if(settings.sampleType() == QAudioFormat::Float) {
199 if(settings.byteOrder() == QAudioFormat::LittleEndian)
200 pcmformat = SND_PCM_FORMAT_FLOAT_LE;
201 else
202 pcmformat = SND_PCM_FORMAT_FLOAT_BE;
203 }
204 } else if(settings.sampleSize() == 64) {
205 if(settings.byteOrder() == QAudioFormat::LittleEndian)
206 pcmformat = SND_PCM_FORMAT_FLOAT64_LE;
207 else
208 pcmformat = SND_PCM_FORMAT_FLOAT64_BE;
209 }
210
211 return pcmformat != SND_PCM_FORMAT_UNKNOWN
212 ? snd_pcm_hw_params_set_format( handle, hwparams, pcmformat)
213 : -1;
214}
215
216QIODevice* QAudioOutputPrivate::start(QIODevice* device)
217{
218 if(deviceState != QAudio::StoppedState)
219 deviceState = QAudio::StoppedState;
220
221 errorState = QAudio::NoError;
222
223 // Handle change of mode
224 if(audioSource && pullMode && !device) {
225 // pull -> push
226 close();
227 audioSource = 0;
228 } else if(audioSource && !pullMode && device) {
229 // push -> pull
230 close();
231 delete audioSource;
232 audioSource = 0;
233 }
234
235 if(device) {
236 //set to pull mode
237 pullMode = true;
238 audioSource = device;
239 deviceState = QAudio::ActiveState;
240 } else {
241 //set to push mode
242 if(!audioSource) {
243 audioSource = new OutputPrivate(this);
244 audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered);
245 }
246 pullMode = false;
247 deviceState = QAudio::IdleState;
248 }
249
250 open();
251
252 emit stateChanged(deviceState);
253
254 return audioSource;
255}
256
257void QAudioOutputPrivate::stop()
258{
259 if(deviceState == QAudio::StoppedState)
260 return;
261 errorState = QAudio::NoError;
262 deviceState = QAudio::StoppedState;
263 close();
264 emit stateChanged(deviceState);
265}
266
267bool QAudioOutputPrivate::open()
268{
269 if(opened)
270 return true;
271
272#ifdef DEBUG_AUDIO
273 QTime now(QTime::currentTime());
274 qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
275#endif
276 timeStamp.restart();
277 elapsedTimeOffset = 0;
278
279 int dir;
280 int err = 0;
281 int count=0;
282 unsigned int freakuency=settings.frequency();
283
284 if (!settings.isValid()) {
285 qWarning("QAudioOutput: open error, invalid format.");
286 } else if (settings.frequency() <= 0) {
287 qWarning("QAudioOutput: open error, invalid sample rate (%d).",
288 settings.frequency());
289 } else {
290 err = -1;
291 }
292
293 if (err == 0) {
294 errorState = QAudio::OpenError;
295 deviceState = QAudio::StoppedState;
296 return false;
297 }
298
299 QString dev = QString(QLatin1String(m_device.constData()));
300 QList<QByteArray> devices = QAudioDeviceInfoInternal::availableDevices(QAudio::AudioOutput);
301 if(dev.compare(QLatin1String("default")) == 0) {
302#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
303 dev = QLatin1String(devices.first());
304#else
305 dev = QLatin1String("hw:0,0");
306#endif
307 } else {
308#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
309 dev = QLatin1String(m_device);
310#else
311 int idx = 0;
312 char *name;
313
314 QString shortName = QLatin1String(m_device.mid(m_device.indexOf('=',0)+1).constData());
315
316 while(snd_card_get_name(idx,&name) == 0) {
317 if(qstrncmp(shortName.toLocal8Bit().constData(),name,shortName.length()) == 0)
318 break;
319 idx++;
320 }
321 dev = QString(QLatin1String("hw:%1,0")).arg(idx);
322#endif
323 }
324
325 // Step 1: try and open the device
326 while((count < 5) && (err < 0)) {
327 err=snd_pcm_open(&handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0);
328 if(err < 0)
329 count++;
330 }
331 if (( err < 0)||(handle == 0)) {
332 errorState = QAudio::OpenError;
333 deviceState = QAudio::StoppedState;
334 return false;
335 }
336 snd_pcm_nonblock( handle, 0 );
337
338 // Step 2: Set the desired HW parameters.
339 snd_pcm_hw_params_alloca( &hwparams );
340
341 bool fatal = false;
342 QString errMessage;
343 unsigned int chunks = 8;
344
345 err = snd_pcm_hw_params_any( handle, hwparams );
346 if ( err < 0 ) {
347 fatal = true;
348 errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_any: err = %1").arg(err);
349 }
350 if ( !fatal ) {
351 err = snd_pcm_hw_params_set_rate_resample( handle, hwparams, 1 );
352 if ( err < 0 ) {
353 fatal = true;
354 errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_rate_resample: err = %1").arg(err);
355 }
356 }
357 if ( !fatal ) {
358 err = snd_pcm_hw_params_set_access( handle, hwparams, access );
359 if ( err < 0 ) {
360 fatal = true;
361 errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_access: err = %1").arg(err);
362 }
363 }
364 if ( !fatal ) {
365 err = setFormat();
366 if ( err < 0 ) {
367 fatal = true;
368 errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_format: err = %1").arg(err);
369 }
370 }
371 if ( !fatal ) {
372 err = snd_pcm_hw_params_set_channels( handle, hwparams, (unsigned int)settings.channels() );
373 if ( err < 0 ) {
374 fatal = true;
375 errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_channels: err = %1").arg(err);
376 }
377 }
378 if ( !fatal ) {
379 err = snd_pcm_hw_params_set_rate_near( handle, hwparams, &freakuency, 0 );
380 if ( err < 0 ) {
381 fatal = true;
382 errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_rate_near: err = %1").arg(err);
383 }
384 }
385 if ( !fatal ) {
386 unsigned int maxBufferTime = 0;
387 unsigned int minBufferTime = 0;
388 unsigned int maxPeriodTime = 0;
389 unsigned int minPeriodTime = 0;
390
391 err = snd_pcm_hw_params_get_buffer_time_max(hwparams, &maxBufferTime, &dir);
392 if ( err >= 0)
393 err = snd_pcm_hw_params_get_buffer_time_min(hwparams, &minBufferTime, &dir);
394 if ( err >= 0)
395 err = snd_pcm_hw_params_get_period_time_max(hwparams, &maxPeriodTime, &dir);
396 if ( err >= 0)
397 err = snd_pcm_hw_params_get_period_time_min(hwparams, &minPeriodTime, &dir);
398
399 if ( err < 0 ) {
400 fatal = true;
401 errMessage = QString::fromLatin1("QAudioOutput: buffer/period min and max: err = %1").arg(err);
402 } else {
403 if (maxBufferTime < buffer_time || buffer_time < minBufferTime || maxPeriodTime < period_time || minPeriodTime > period_time) {
404#ifdef DEBUG_AUDIO
405 qDebug()<<"defaults out of range";
406 qDebug()<<"pmin="<<minPeriodTime<<", pmax="<<maxPeriodTime<<", bmin="<<minBufferTime<<", bmax="<<maxBufferTime;
407#endif
408 period_time = minPeriodTime;
409 if (period_time*4 <= maxBufferTime) {
410 // Use 4 periods if possible
411 buffer_time = period_time*4;
412 chunks = 4;
413 } else if (period_time*2 <= maxBufferTime) {
414 // Use 2 periods if possible
415 buffer_time = period_time*2;
416 chunks = 2;
417 } else {
418 qWarning()<<"QAudioOutput: alsa only supports single period!";
419 fatal = true;
420 }
421#ifdef DEBUG_AUDIO
422 qDebug()<<"used: buffer_time="<<buffer_time<<", period_time="<<period_time;
423#endif
424 }
425 }
426 }
427 if ( !fatal ) {
428 err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir);
429 if ( err < 0 ) {
430 fatal = true;
431 errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_buffer_time_near: err = %1").arg(err);
432 }
433 }
434 if ( !fatal ) {
435 err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir);
436 if ( err < 0 ) {
437 fatal = true;
438 errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_period_time_near: err = %1").arg(err);
439 }
440 }
441 if ( !fatal ) {
442 err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &chunks, &dir);
443 if ( err < 0 ) {
444 fatal = true;
445 errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_periods_near: err = %1").arg(err);
446 }
447 }
448 if ( !fatal ) {
449 err = snd_pcm_hw_params(handle, hwparams);
450 if ( err < 0 ) {
451 fatal = true;
452 errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params: err = %1").arg(err);
453 }
454 }
455 if( err < 0) {
456 qWarning()<<errMessage;
457 errorState = QAudio::OpenError;
458 deviceState = QAudio::StoppedState;
459 return false;
460 }
461 snd_pcm_hw_params_get_buffer_size(hwparams,&buffer_frames);
462 buffer_size = snd_pcm_frames_to_bytes(handle,buffer_frames);
463 snd_pcm_hw_params_get_period_size(hwparams,&period_frames, &dir);
464 period_size = snd_pcm_frames_to_bytes(handle,period_frames);
465 snd_pcm_hw_params_get_buffer_time(hwparams,&buffer_time, &dir);
466 snd_pcm_hw_params_get_period_time(hwparams,&period_time, &dir);
467
468 // Step 3: Set the desired SW parameters.
469 snd_pcm_sw_params_t *swparams;
470 snd_pcm_sw_params_alloca(&swparams);
471 snd_pcm_sw_params_current(handle, swparams);
472 snd_pcm_sw_params_set_start_threshold(handle,swparams,period_frames);
473 snd_pcm_sw_params_set_stop_threshold(handle,swparams,buffer_frames);
474 snd_pcm_sw_params_set_avail_min(handle, swparams,period_frames);
475 snd_pcm_sw_params(handle, swparams);
476
477 // Step 4: Prepare audio
478 if(audioBuffer == 0)
479 audioBuffer = new char[snd_pcm_frames_to_bytes(handle,buffer_frames)];
480 snd_pcm_prepare( handle );
481 snd_pcm_start(handle);
482
483 // Step 5: Setup callback and timer fallback
484 snd_async_add_pcm_handler(&ahandler, handle, async_callback, this);
485 bytesAvailable = bytesFree();
486
487 // Step 6: Start audio processing
488 timer->start(period_time/1000);
489
490 clockStamp.restart();
491 timeStamp.restart();
492 elapsedTimeOffset = 0;
493 errorState = QAudio::NoError;
494 totalTimeValue = 0;
495 opened = true;
496
497 return true;
498}
499
500void QAudioOutputPrivate::close()
501{
502 timer->stop();
503
504 if ( handle ) {
505 snd_pcm_drain( handle );
506 snd_pcm_close( handle );
507 handle = 0;
508 delete [] audioBuffer;
509 audioBuffer=0;
510 }
511 if(!pullMode && audioSource) {
512 delete audioSource;
513 audioSource = 0;
514 }
515 opened = false;
516}
517
518int QAudioOutputPrivate::bytesFree() const
519{
520 if(resuming)
521 return period_size;
522
523 if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState)
524 return 0;
525 int frames = snd_pcm_avail_update(handle);
526 if((int)frames > (int)buffer_frames)
527 frames = buffer_frames;
528
529 return snd_pcm_frames_to_bytes(handle, frames);
530}
531
532qint64 QAudioOutputPrivate::write( const char *data, qint64 len )
533{
534 // Write out some audio data
535 if ( !handle )
536 return 0;
537#ifdef DEBUG_AUDIO
538 qDebug()<<"frames to write out = "<<
539 snd_pcm_bytes_to_frames( handle, (int)len )<<" ("<<len<<") bytes";
540#endif
541 int frames, err;
542 int space = bytesFree();
543 if(len < space) {
544 // Just write it
545 frames = snd_pcm_bytes_to_frames( handle, (int)len );
546 err = snd_pcm_writei( handle, data, frames );
547 } else {
548 // Only write space worth
549 frames = snd_pcm_bytes_to_frames( handle, (int)space );
550 err = snd_pcm_writei( handle, data, frames );
551 }
552 if(err > 0) {
553 totalTimeValue += err;
554 resuming = false;
555 errorState = QAudio::NoError;
556 if (deviceState != QAudio::ActiveState) {
557 deviceState = QAudio::ActiveState;
558 emit stateChanged(deviceState);
559 }
560 return snd_pcm_frames_to_bytes( handle, err );
561 } else
562 err = xrun_recovery(err);
563
564 if(err < 0) {
565 close();
566 errorState = QAudio::FatalError;
567 deviceState = QAudio::StoppedState;
568 emit stateChanged(deviceState);
569 }
570 return 0;
571}
572
573int QAudioOutputPrivate::periodSize() const
574{
575 return period_size;
576}
577
578void QAudioOutputPrivate::setBufferSize(int value)
579{
580 if(deviceState == QAudio::StoppedState)
581 buffer_size = value;
582}
583
584int QAudioOutputPrivate::bufferSize() const
585{
586 return buffer_size;
587}
588
589void QAudioOutputPrivate::setNotifyInterval(int ms)
590{
591 intervalTime = qMax(0, ms);
592}
593
594int QAudioOutputPrivate::notifyInterval() const
595{
596 return intervalTime;
597}
598
599qint64 QAudioOutputPrivate::processedUSecs() const
600{
601 return qint64(1000000) * totalTimeValue / settings.frequency();
602}
603
604void QAudioOutputPrivate::resume()
605{
606 if(deviceState == QAudio::SuspendedState) {
607 int err = 0;
608
609 if(handle) {
610 err = snd_pcm_prepare( handle );
611 if(err < 0)
612 xrun_recovery(err);
613
614 err = snd_pcm_start(handle);
615 if(err < 0)
616 xrun_recovery(err);
617
618 bytesAvailable = (int)snd_pcm_frames_to_bytes(handle, buffer_frames);
619 }
620 resuming = true;
621
622 deviceState = QAudio::ActiveState;
623
624 errorState = QAudio::NoError;
625 timer->start(period_time/1000);
626 emit stateChanged(deviceState);
627 }
628}
629
630QAudioFormat QAudioOutputPrivate::format() const
631{
632 return settings;
633}
634
635void QAudioOutputPrivate::suspend()
636{
637 if(deviceState == QAudio::ActiveState || deviceState == QAudio::IdleState || resuming) {
638 timer->stop();
639 deviceState = QAudio::SuspendedState;
640 errorState = QAudio::NoError;
641 emit stateChanged(deviceState);
642 }
643}
644
645void QAudioOutputPrivate::userFeed()
646{
647 if(deviceState == QAudio::StoppedState || deviceState == QAudio::SuspendedState)
648 return;
649#ifdef DEBUG_AUDIO
650 QTime now(QTime::currentTime());
651 qDebug()<<now.second()<<"s "<<now.msec()<<"ms :userFeed() OUT";
652#endif
653 if(deviceState == QAudio::IdleState)
654 bytesAvailable = bytesFree();
655
656 deviceReady();
657}
658
659void QAudioOutputPrivate::feedback()
660{
661 updateAvailable();
662}
663
664
665void QAudioOutputPrivate::updateAvailable()
666{
667#ifdef DEBUG_AUDIO
668 QTime now(QTime::currentTime());
669 qDebug()<<now.second()<<"s "<<now.msec()<<"ms :updateAvailable()";
670#endif
671 bytesAvailable = bytesFree();
672}
673
674bool QAudioOutputPrivate::deviceReady()
675{
676 if(pullMode) {
677 int l = 0;
678 int chunks = bytesAvailable/period_size;
679 if(chunks==0) {
680 bytesAvailable = bytesFree();
681 return false;
682 }
683#ifdef DEBUG_AUDIO
684 qDebug()<<"deviceReady() avail="<<bytesAvailable<<" bytes, period size="<<period_size<<" bytes";
685 qDebug()<<"deviceReady() no. of chunks that can fit ="<<chunks<<", chunks in bytes ="<<period_size*chunks;
686#endif
687 int input = period_frames*chunks;
688 if(input > (int)buffer_frames)
689 input = buffer_frames;
690 l = audioSource->read(audioBuffer,snd_pcm_frames_to_bytes(handle, input));
691 if(l > 0) {
692 // Got some data to output
693 if(deviceState != QAudio::ActiveState)
694 return true;
695 qint64 bytesWritten = write(audioBuffer,l);
696 if (bytesWritten != l)
697 audioSource->seek(audioSource->pos()-(l-bytesWritten));
698 bytesAvailable = bytesFree();
699
700 } else if(l == 0) {
701 // Did not get any data to output
702 bytesAvailable = bytesFree();
703 if(bytesAvailable > snd_pcm_frames_to_bytes(handle, buffer_frames-period_frames)) {
704 // Underrun
705 if (deviceState != QAudio::IdleState) {
706 errorState = QAudio::UnderrunError;
707 deviceState = QAudio::IdleState;
708 emit stateChanged(deviceState);
709 }
710 }
711
712 } else if(l < 0) {
713 close();
714 deviceState = QAudio::StoppedState;
715 errorState = QAudio::IOError;
716 emit stateChanged(deviceState);
717 }
718 } else {
719 bytesAvailable = bytesFree();
720 if(bytesAvailable > snd_pcm_frames_to_bytes(handle, buffer_frames-period_frames)) {
721 // Underrun
722 if (deviceState != QAudio::IdleState) {
723 errorState = QAudio::UnderrunError;
724 deviceState = QAudio::IdleState;
725 emit stateChanged(deviceState);
726 }
727 }
728 }
729
730 if(deviceState != QAudio::ActiveState)
731 return true;
732
733 if(intervalTime && (timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) {
734 emit notify();
735 elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime;
736 timeStamp.restart();
737 }
738 return true;
739}
740
741qint64 QAudioOutputPrivate::elapsedUSecs() const
742{
743 if (deviceState == QAudio::StoppedState)
744 return 0;
745
746 return clockStamp.elapsed()*1000;
747}
748
749void QAudioOutputPrivate::reset()
750{
751 if(handle)
752 snd_pcm_reset(handle);
753
754 stop();
755}
756
757OutputPrivate::OutputPrivate(QAudioOutputPrivate* audio)
758{
759 audioDevice = qobject_cast<QAudioOutputPrivate*>(audio);
760}
761
762OutputPrivate::~OutputPrivate() {}
763
764qint64 OutputPrivate::readData( char* data, qint64 len)
765{
766 Q_UNUSED(data)
767 Q_UNUSED(len)
768
769 return 0;
770}
771
772qint64 OutputPrivate::writeData(const char* data, qint64 len)
773{
774 int retry = 0;
775 qint64 written = 0;
776 if((audioDevice->deviceState == QAudio::ActiveState)
777 ||(audioDevice->deviceState == QAudio::IdleState)) {
778 while(written < len) {
779 int chunk = audioDevice->write(data+written,(len-written));
780 if(chunk <= 0)
781 retry++;
782 written+=chunk;
783 if(retry > 10)
784 return written;
785 }
786 }
787 return written;
788
789}
790
791QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.