bool QAudioOutputPrivate::deviceReady() { if(pullMode) { int l = 0; int chunks = bytesAvailable/period_size; if(chunks==0) { bytesAvailable = bytesFree(); return false; } #ifdef DEBUG_AUDIO qDebug()<<"deviceReady() avail="<<bytesAvailable<<" bytes, period size="<<period_size<<" bytes"; qDebug()<<"deviceReady() no. of chunks that can fit ="<<chunks<<", chunks in bytes ="<<period_size*chunks; #endif int input = period_frames*chunks; if(input > (int)buffer_frames) input = buffer_frames; l = audioSource->read(audioBuffer,snd_pcm_frames_to_bytes(handle, input)); if(l > 0) { // Got some data to output if(deviceState != QAudio::ActiveState) return true; write(audioBuffer,l); bytesAvailable = bytesFree(); } else if(l == 0) { // Did not get any data to output bytesAvailable = bytesFree(); if(bytesAvailable > snd_pcm_frames_to_bytes(handle, buffer_frames-period_frames)) { // Underrun errorState = QAudio::UnderrunError; deviceState = QAudio::IdleState; emit stateChanged(deviceState); } } else if(l < 0) { close(); errorState = QAudio::IOError; emit stateChanged(deviceState); } } else bytesAvailable = bytesFree(); if(deviceState != QAudio::ActiveState) return true; if((timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) { emit notify(); elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime; timeStamp.restart(); } return true; }
void AudioOutput::write() { int freeBytes = bytesFree(); if(freeBytes <= 0) return; /*first, send the remain data of audio buffer*/ int remain = size; //write remain data if(remain < freeBytes && remain > 0){ writeData(remain); freeBytes -= remain; } /*second, convert the avframe to the audio buffer and send to the device*/ while(freeBytes > 0 && aw->audioFrameQ.size() > 0){ //convert AVframe data to audio buffer std::shared_ptr<Frame> sharedFrame; if(!aw->audioFrameQ.pop(sharedFrame)) return; if(sharedFrame->serial != aw->serial) continue; readAVFrame(sharedFrame->frame); if(freeBytes <= size){ writeData(freeBytes); freeBytes = 0; } else{ int len = size; writeData(len); freeBytes -= len; } } }
void QAudioOutputPrivate::updateAvailable() { #ifdef DEBUG_AUDIO QTime now(QTime::currentTime()); qDebug()<<now.second()<<"s "<<now.msec()<<"ms :updateAvailable()"; #endif bytesAvailable = bytesFree(); }
void QAudioOutputPrivate::suspend() { if(deviceState == QAudio::ActiveState || deviceState == QAudio::IdleState) { int delay = (buffer_size-bytesFree())*1000/(settings.frequency() *settings.channels()*(settings.sampleSize()/8)); waveOutPause(hWaveOut); Sleep(delay+10); deviceState = QAudio::SuspendedState; errorState = QAudio::NoError; emit stateChanged(deviceState); } }
void QAudioOutputPrivate::userFeed() { if(deviceState == QAudio::StoppedState || deviceState == QAudio::SuspendedState) return; #ifdef DEBUG_AUDIO QTime now(QTime::currentTime()); qDebug()<<now.second()<<"s "<<now.msec()<<"ms :userFeed() OUT"; #endif if(deviceState == QAudio::IdleState) bytesAvailable = bytesFree(); deviceReady(); }
void QAudioOutputPrivate::feedback() { #ifdef DEBUG_AUDIO QTime now(QTime::currentTime()); qDebug()<<now.second()<<"s "<<now.msec()<<"ms :feedback()"; #endif bytesAvailable = bytesFree(); if(!(deviceState==QAudio::StoppedState||deviceState==QAudio::SuspendedState)) { if(bytesAvailable >= period_size) QMetaObject::invokeMethod(this, "deviceReady", Qt::QueuedConnection); } }
void QAudioOutputPrivate::close() { if(deviceState == QAudio::StoppedState) return; deviceState = QAudio::StoppedState; errorState = QAudio::NoError; int delay = (buffer_size-bytesFree())*1000/(settings.frequency() *settings.channels()*(settings.sampleSize()/8)); waveOutReset(hWaveOut); Sleep(delay+10); freeBlocks(waveBlocks); waveOutClose(hWaveOut); delete [] audioBuffer; audioBuffer = 0; buffer_size = 0; }
qint64 QAudioOutputPrivate::write( const char *data, qint64 len ) { // Write out some audio data if ( !handle ) return 0; #ifdef DEBUG_AUDIO qDebug()<<"frames to write out = "<< snd_pcm_bytes_to_frames( handle, (int)len )<<" ("<<len<<") bytes"; #endif int frames, err; int space = bytesFree(); if(len < space) { // Just write it frames = snd_pcm_bytes_to_frames( handle, (int)len ); err = snd_pcm_writei( handle, data, frames ); } else { // Only write space worth frames = snd_pcm_bytes_to_frames( handle, (int)space ); err = snd_pcm_writei( handle, data, frames ); } if(err > 0) { totalTimeValue += err; resuming = false; errorState = QAudio::NoError; if (deviceState != QAudio::ActiveState) { deviceState = QAudio::ActiveState; emit stateChanged(deviceState); } return snd_pcm_frames_to_bytes( handle, err ); } else err = xrun_recovery(err); if(err < 0) { close(); errorState = QAudio::FatalError; emit errorChanged(errorState); deviceState = QAudio::StoppedState; emit stateChanged(deviceState); } return 0; }
void Buffer::writeData(const uint8_t* pData, uint32_t size) { if (size > bytesFree()) { throw std::logic_error("Not enough room in audio buffer to write data"); } if (m_pWritePtr + size >= m_pEnd) { uint32_t firstSize = static_cast<uint32_t>(m_pEnd - m_pWritePtr); uint32_t secondSize = size - firstSize; memcpy(m_pWritePtr, pData, firstSize); memcpy(m_pAudioBuffer, pData + firstSize, secondSize); m_pWritePtr = m_pAudioBuffer + secondSize; } else { memcpy(m_pWritePtr, pData, size); m_pWritePtr += size; } m_Fill += size; }
bool QAudioOutputPrivate::open() { if(opened) return true; #ifdef DEBUG_AUDIO QTime now(QTime::currentTime()); qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; #endif timeStamp.restart(); elapsedTimeOffset = 0; int dir; int err=-1; int count=0; unsigned int freakuency=settings.frequency(); QString dev = QLatin1String(m_device.constData()); QList<QByteArray> devices = QAudioDeviceInfoInternal::availableDevices(QAudio::AudioOutput); if(dev.compare(QLatin1String("default")) == 0) { #if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) dev = QLatin1String(devices.first().constData()); #else dev = QLatin1String("hw:0,0"); #endif } else { #if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) dev = QLatin1String(m_device); #else int idx = 0; char *name; QString shortName = QLatin1String(m_device.mid(m_device.indexOf('=',0)+1).constData()); while(snd_card_get_name(idx,&name) == 0) { if(qstrncmp(shortName.toLocal8Bit().constData(),name,shortName.length()) == 0) break; idx++; } dev = QString(QLatin1String("hw:%1,0")).arg(idx); #endif } // Step 1: try and open the device while((count < 5) && (err < 0)) { err=snd_pcm_open(&handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0); if(err < 0) count++; } if (( err < 0)||(handle == 0)) { errorState = QAudio::OpenError; deviceState = QAudio::StoppedState; return false; } snd_pcm_nonblock( handle, 0 ); // Step 2: Set the desired HW parameters. snd_pcm_hw_params_alloca( &hwparams ); bool fatal = false; QString errMessage; unsigned int chunks = 8; err = snd_pcm_hw_params_any( handle, hwparams ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_any: err = %1").arg(err); } if ( !fatal ) { err = snd_pcm_hw_params_set_rate_resample( handle, hwparams, 1 ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_rate_resample: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_access( handle, hwparams, access ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_access: err = %1").arg(err); } } if ( !fatal ) { err = setFormat(); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_format: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_channels( handle, hwparams, (unsigned int)settings.channels() ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_channels: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_rate_near( handle, hwparams, &freakuency, 0 ); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_rate_near: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_buffer_time_near: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_period_time_near: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &chunks, &dir); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_periods_near: err = %1").arg(err); } } if ( !fatal ) { err = snd_pcm_hw_params(handle, hwparams); if ( err < 0 ) { fatal = true; errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params: err = %1").arg(err); } } if( err < 0) { qWarning()<<errMessage; errorState = QAudio::OpenError; deviceState = QAudio::StoppedState; return false; } snd_pcm_hw_params_get_buffer_size(hwparams,&buffer_frames); buffer_size = snd_pcm_frames_to_bytes(handle,buffer_frames); snd_pcm_hw_params_get_period_size(hwparams,&period_frames, &dir); period_size = snd_pcm_frames_to_bytes(handle,period_frames); snd_pcm_hw_params_get_buffer_time(hwparams,&buffer_time, &dir); snd_pcm_hw_params_get_period_time(hwparams,&period_time, &dir); // Step 3: Set the desired SW parameters. snd_pcm_sw_params_t *swparams; snd_pcm_sw_params_alloca(&swparams); snd_pcm_sw_params_current(handle, swparams); snd_pcm_sw_params_set_start_threshold(handle,swparams,period_frames); snd_pcm_sw_params_set_stop_threshold(handle,swparams,buffer_frames); snd_pcm_sw_params_set_avail_min(handle, swparams,period_frames); snd_pcm_sw_params(handle, swparams); // Step 4: Prepare audio if(audioBuffer == 0) audioBuffer = new char[snd_pcm_frames_to_bytes(handle,buffer_frames)]; snd_pcm_prepare( handle ); snd_pcm_start(handle); // Step 5: Setup callback and timer fallback snd_async_add_pcm_handler(&ahandler, handle, async_callback, this); bytesAvailable = bytesFree(); // Step 6: Start audio processing timer->start(period_time/1000); clockStamp.restart(); timeStamp.restart(); elapsedTimeOffset = 0; errorState = QAudio::NoError; totalTimeValue = 0; opened = true; return true; }
bool QAudioOutputPrivate::deviceReady() { if(deviceState == QAudio::StoppedState) return false; if(pullMode) { int chunks = bytesAvailable/period_size; #ifdef DEBUG_AUDIO qDebug()<<"deviceReady() avail="<<bytesAvailable<<" bytes, period size="<<period_size<<" bytes"; qDebug()<<"deviceReady() no. of chunks that can fit ="<<chunks<<", chunks in bytes ="<<chunks*period_size; #endif bool startup = false; if(totalTimeValue == 0) startup = true; bool full=false; EnterCriticalSection(&waveOutCriticalSection); if(waveFreeBlockCount==0) full = true; LeaveCriticalSection(&waveOutCriticalSection); if (full){ #ifdef DEBUG_AUDIO qDebug() << "Skipping data as unable to write"; #endif if((timeStamp.elapsed() + elapsedTimeOffset) > intervalTime ) { emit notify(); elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime; timeStamp.restart(); } return true; } if(startup) waveOutPause(hWaveOut); int input = period_size*chunks; int l = audioSource->read(audioBuffer,input); if(l > 0) { int out= write(audioBuffer,l); if(out > 0) { if (deviceState != QAudio::ActiveState) { deviceState = QAudio::ActiveState; emit stateChanged(deviceState); } } if ( out < l) { // Didnt write all data audioSource->seek(audioSource->pos()-(l-out)); } if(startup) waveOutRestart(hWaveOut); } else if(l == 0) { bytesAvailable = bytesFree(); int check = 0; EnterCriticalSection(&waveOutCriticalSection); check = waveFreeBlockCount; LeaveCriticalSection(&waveOutCriticalSection); if(check == buffer_size/period_size) { errorState = QAudio::UnderrunError; if (deviceState != QAudio::IdleState) { deviceState = QAudio::IdleState; emit stateChanged(deviceState); } } } else if(l < 0) { bytesAvailable = bytesFree(); errorState = QAudio::IOError; } } else { int buffered; EnterCriticalSection(&waveOutCriticalSection); buffered = waveFreeBlockCount; LeaveCriticalSection(&waveOutCriticalSection); errorState = QAudio::UnderrunError; if (buffered >= buffer_size/period_size && deviceState == QAudio::ActiveState) { deviceState = QAudio::IdleState; emit stateChanged(deviceState); } } if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState) return true; if((timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) { emit notify(); elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime; timeStamp.restart(); } return true; }