at5 audio

requires qtmultimedia5-dev
This commit is contained in:
Hoernchen 2013-07-30 14:39:07 +02:00
parent 99466ed1d9
commit fca9f8870f
9 changed files with 219 additions and 185 deletions

View File

@ -11,6 +11,7 @@ set(CMAKE_AUTOMOC ON)
#find_package(Qt4 REQUIRED)
find_package(Qt5Core 5.0 REQUIRED)
find_package(Qt5Widgets 5.0 REQUIRED)
find_package(Qt5Multimedia 5.0 REQUIRED)
#find_package(QT5OpenGL 5.0 REQUIRED)
find_package(OpenGL REQUIRED)
find_package(PkgConfig)
@ -219,7 +220,7 @@ endif(FFTW3F_FOUND)
set_target_properties(sdrbase PROPERTIES DEFINE_SYMBOL "sdrangelove_EXPORTS")
qt5_use_modules(sdrbase Core Widgets OpenGL)
qt5_use_modules(sdrbase Core Widgets OpenGL Multimedia)
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
@ -264,7 +265,7 @@ if(WIN32)
set_target_properties(sdrangelove PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
endif(WIN32)
qt5_use_modules(sdrangelove Widgets)
qt5_use_modules(sdrangelove Widgets Multimedia)
##############################################################################

View File

@ -18,30 +18,58 @@
#ifndef INCLUDE_AUDIOOUTPUT_H
#define INCLUDE_AUDIOOUTPUT_H
#include <QIODevice>
#include <QThread>
#include <QMutex>
#include <QAudioOutput>
#include <list>
#include <vector>
#include "portaudio.h"
#include "util/export.h"
class AudioFifo;
class AudioOutput;
class SDRANGELOVE_API AudioOutput {
class SoundThread : public QThread
{
Q_OBJECT
public:
explicit SoundThread(AudioOutput* out, QObject *parent = 0);
~SoundThread();
void run();
signals:
public slots:
void play();
void stop();
void kill();
private:
void playInt();
AudioOutput* m_generator;
QAudioOutput* m_audioOutput;
};
class SDRANGELOVE_API AudioOutput : public QIODevice{
Q_OBJECT
public:
AudioOutput();
~AudioOutput();
void start();
bool start(int device, int rate);
void stop();
void addFifo(AudioFifo* audioFifo);
void removeFifo(AudioFifo* audioFifo);
qint64 readData(char *data, qint64 maxlen);
qint64 writeData(const char *data, qint64 len);
//int bufferedSamples();
private:
QMutex m_mutex;
PaStream* m_stream;
//AudioFifo* m_audioFifo;
typedef std::list<AudioFifo*> AudioFifos;
AudioFifos m_audioFifos;
@ -50,20 +78,10 @@ private:
int m_sampleRate;
//PaTime m_streamStartTime;
static int callbackHelper(
const void* inputBuffer,
void* outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* userData);
SoundThread _sfxThread;
int callback(
const void* inputBuffer,
void* outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags);
int callback(void* outputBuffer,
unsigned long framesPerBuffer);
};
#endif // INCLUDE_AUDIOOUTPUT_H

View File

@ -44,4 +44,4 @@ target_link_libraries(demodnfm
sdrbase
)
qt5_use_modules(demodnfm Core Widgets OpenGL)
qt5_use_modules(demodnfm Core Widgets OpenGL Multimedia)

View File

@ -44,4 +44,4 @@ target_link_libraries(demodtetra
sdrbase
)
qt5_use_modules(demodtetra Core Widgets OpenGL)
qt5_use_modules(demodtetra Core Widgets OpenGL Multimedia)

View File

@ -47,4 +47,4 @@ target_link_libraries(inputosmosdr
sdrbase
)
qt5_use_modules(inputosmosdr Core Widgets OpenGL)
qt5_use_modules(inputosmosdr Core Widgets OpenGL Multimedia)

View File

@ -47,4 +47,4 @@ target_link_libraries(inputrtlsdr
sdrbase
)
qt5_use_modules(inputrtlsdr Core Widgets OpenGL)
qt5_use_modules(inputrtlsdr Core Widgets OpenGL Multimedia)

View File

@ -16,39 +16,38 @@
///////////////////////////////////////////////////////////////////////////////////
#include "audio/audiodeviceinfo.h"
#include <portaudio.h>
AudioDeviceInfo::AudioDeviceInfo()
{
const PaDeviceInfo *deviceInfo;
const PaHostApiInfo *apiInfo;
PaError err;
int numDevices;
int i;
if((numDevices = Pa_GetDeviceCount()) < 0) {
err = numDevices;
goto failed;
}
m_devices.clear();
for(i = 0; i < numDevices; i++) {
deviceInfo = Pa_GetDeviceInfo(i);
if(deviceInfo->maxOutputChannels >= 2) {
apiInfo = Pa_GetHostApiInfo(deviceInfo->hostApi);
m_devices.append(Device(
QString::fromLatin1(deviceInfo->name),
QString::fromLatin1(apiInfo->name),
i));
}
}
qDebug("Audio initialisation: %d devices found", m_devices.count());
return;
failed:
if(err != paNoError)
qCritical("Audio initialisation failed: %s (%d)", Pa_GetErrorText(err), err);
// const PaDeviceInfo *deviceInfo;
// const PaHostApiInfo *apiInfo;
// PaError err;
// int numDevices;
// int i;
//
// if((numDevices = Pa_GetDeviceCount()) < 0) {
// err = numDevices;
// goto failed;
// }
//
// m_devices.clear();
//
// for(i = 0; i < numDevices; i++) {
// deviceInfo = Pa_GetDeviceInfo(i);
// if(deviceInfo->maxOutputChannels >= 2) {
// apiInfo = Pa_GetHostApiInfo(deviceInfo->hostApi);
// m_devices.append(Device(
// QString::fromLatin1(deviceInfo->name),
// QString::fromLatin1(apiInfo->name),
// i));
// }
// }
// qDebug("Audio initialisation: %d devices found", m_devices.count());
// return;
//
//failed:
// if(err != paNoError)
// qCritical("Audio initialisation failed: %s (%d)", Pa_GetErrorText(err), err);
}
int AudioDeviceInfo::match(const QString& api, const QString device) const

View File

@ -19,17 +19,93 @@
#include "audio/audiooutput.h"
#include "audio/audiofifo.h"
SoundThread::SoundThread(AudioOutput* out, QObject *parent) :
m_generator(out)
, m_audioOutput(0)
, QThread(parent)
{
start();
// Move event processing of SoundThread to this thread
QObject::moveToThread(this);
}
SoundThread::~SoundThread()
{
stop();
//delete m_audioOutput;
quit();
wait();
}
void SoundThread::stop()
{
m_audioOutput->stop();
}
void SoundThread::kill()
{
m_audioOutput->stop();
delete m_audioOutput;
}
void SoundThread::play()
{
playInt();
}
void SoundThread::playInt()
{
//connect(m_audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(stateChanged(QAudio::State)));
m_generator->start();
m_audioOutput->start(m_generator);
}
void SoundThread::run()
{
QAudioFormat m_format;
QList<QAudioDeviceInfo> foo = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
QAudioDeviceInfo m_device(foo[0]);
m_format.setSampleRate(44100);
m_format.setChannelCount(2);
m_format.setSampleSize(16);
m_format.setCodec("audio/pcm");
m_format.setByteOrder(QAudioFormat::LittleEndian);
m_format.setSampleType(QAudioFormat::SignedInt);
QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
if (!info.isFormatSupported(m_format)) {
qWarning("Default format not supported - trying to use nearest");
m_format = info.nearestFormat(m_format);
}
//m_generator = new Generator(m_format, DurationSeconds*1000000, ToneFrequencyHz, this);
delete m_audioOutput;
m_audioOutput = 0;
m_audioOutput = new QAudioOutput(m_device, m_format, this);
//m_audioOutput->setBufferSize(16384);
exec();
}
AudioOutput::AudioOutput() :
m_mutex(),
m_stream(NULL),
m_audioFifos(),
m_sampleRate(0)
m_sampleRate(0),
_sfxThread(this)
{
}
AudioOutput::~AudioOutput()
{
stop();
QMetaObject::invokeMethod(&_sfxThread, "kill", Qt::QueuedConnection);
QMutexLocker mutexLocker(&m_mutex);
for(AudioFifos::iterator it = m_audioFifos.begin(); it != m_audioFifos.end(); ++it)
@ -40,61 +116,80 @@ AudioOutput::~AudioOutput()
bool AudioOutput::start(int device, int rate)
{
QMutexLocker mutexLocker(&m_mutex);
if(m_stream != NULL) {
Pa_StopStream(m_stream);
Pa_CloseStream(m_stream);
m_stream = NULL;
m_sampleRate = 0;
}
for(AudioFifos::iterator it = m_audioFifos.begin(); it != m_audioFifos.end(); ++it)
(*it)->clear();
PaStreamParameters outputParameters;
const PaStreamInfo* streamInfo;
PaError err;
outputParameters.device = device;
outputParameters.channelCount = 2;
outputParameters.sampleFormat = paInt16;
outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
qDebug("AudioOutput: open");
if((err = Pa_OpenStream(&m_stream, NULL, &outputParameters, rate, 1024, paClipOff, callbackHelper, this)) != paNoError)
goto failed;
qDebug("AudioOutput: start");
// m_streamStartTime = Pa_GetStreamTime(m_stream);
if((err = Pa_StartStream(m_stream)) != paNoError)
goto failed;
streamInfo = Pa_GetStreamInfo(m_stream);
m_sampleRate = streamInfo->sampleRate;
qDebug("AudioOutput: playback has started (%s @ %d Hz)", Pa_GetDeviceInfo(outputParameters.device)->name, rate);
QMetaObject::invokeMethod(&_sfxThread, "play", Qt::QueuedConnection);
return true;
}
failed:
qCritical("AudioOutput: playback failed: %s (%d)", Pa_GetErrorText(err), err);
Pa_CloseStream(m_stream);
m_stream = NULL;
m_sampleRate = 0;
return false;
void AudioOutput::start()
{
open(QIODevice::ReadOnly);
}
void AudioOutput::stop()
{
m_mutex.lock();
if(m_stream != NULL) {
m_mutex.unlock();
Pa_StopStream(m_stream);
m_mutex.lock();
Pa_CloseStream(m_stream);
m_stream = NULL;
m_sampleRate = 0;
QMutexLocker mutexLocker(&m_mutex);
QMetaObject::invokeMethod(&_sfxThread, "stop", Qt::QueuedConnection);
qDebug("AudioOutput: stopped");
close();
}
qint64 AudioOutput::readData(char *data, qint64 len)
{
int reallen = len/4;
QMutexLocker mutexLocker(&m_mutex);
if(m_mixBuffer.size() != reallen * 2) {
m_mixBuffer.resize(reallen * 2); // allocate 2 qint32 per frame (stereo)
if(m_mixBuffer.size() != reallen * 2)
return 0;
}
m_mutex.unlock();
memset(m_mixBuffer.data(), 0x00, m_mixBuffer.size() * sizeof(m_mixBuffer[0])); // start with silence
// sum up a block from all fifos
for(AudioFifos::iterator it = m_audioFifos.begin(); it != m_audioFifos.end(); ++it) {
// use outputBuffer as temp - yes, one memcpy could be saved
uint samples = (*it)->read((quint8*)data, reallen, 0);
const qint16* src = (const qint16*)data;
std::vector<qint32>::iterator dst = m_mixBuffer.begin();
for(uint i = 0; i < samples; i++) {
*dst += *src;
++src;
++dst;
*dst += *src;
++src;
++dst;
}
}
// convert to int16
std::vector<qint32>::const_iterator src = m_mixBuffer.begin();
qint16* dst = (qint16*)data;
for(uint i = 0; i < reallen; ++i) {
qint32 s = *src++;
if(s < -32768)
s = -32768;
else if(s > 32767)
s = 32767;
*dst++ = s;
}
//qDebug("AudioOutput: read %d", len);
return len;
}
qint64 AudioOutput::writeData(const char *data, qint64 len)
{
Q_UNUSED(data);
Q_UNUSED(len);
qDebug("AudioOutput: watwatwatwatwat??!");
return 0;
}
void AudioOutput::addFifo(AudioFifo* audioFifo)
@ -111,82 +206,3 @@ void AudioOutput::removeFifo(AudioFifo* audioFifo)
m_audioFifos.remove(audioFifo);
}
/*
int AudioOutput::bufferedSamples()
{
return (Pa_GetStreamTime(m_stream) - m_streamStartTime) * m_sampleRate;
}
*/
int AudioOutput::callbackHelper(
const void *inputBuffer,
void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData)
{
AudioOutput* audioOutput = (AudioOutput*)userData;
if(audioOutput == NULL)
return paAbort;
if(outputBuffer == NULL)
return paAbort;
return audioOutput->callback(inputBuffer, outputBuffer, framesPerBuffer, timeInfo, statusFlags);
}
int AudioOutput::callback(
const void* inputBuffer,
void* outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags)
{
QMutexLocker mutexLocker(&m_mutex);
Q_UNUSED(inputBuffer);
Q_UNUSED(timeInfo);
Q_UNUSED(statusFlags);
if(m_mixBuffer.size() != framesPerBuffer * 2) {
m_mixBuffer.resize(framesPerBuffer * 2); // allocate 2 qint32 per frame (stereo)
if(m_mixBuffer.size() != framesPerBuffer * 2)
return paAbort;
}
memset(m_mixBuffer.data(), 0x00, m_mixBuffer.size() * sizeof(m_mixBuffer[0])); // start with silence
// sum up a block from all fifos
for(AudioFifos::iterator it = m_audioFifos.begin(); it != m_audioFifos.end(); ++it) {
// use outputBuffer as temp - yes, one memcpy could be saved
uint samples = (*it)->read((quint8*)outputBuffer, framesPerBuffer, 0);
const qint16* src = (const qint16*)outputBuffer;
std::vector<qint32>::iterator dst = m_mixBuffer.begin();
for(int i = 0; i < samples; i++) {
*dst += *src;
++src;
++dst;
*dst += *src;
++src;
++dst;
}
}
// convert to int16
std::vector<qint32>::const_iterator src = m_mixBuffer.begin();
qint16* dst = (qint16*)outputBuffer;
for(int i = 0; i < framesPerBuffer; ++i) {
qint32 s = *src++;
if(s < -32768)
s = -32768;
else if(s > 32767)
s = 32767;
*dst++ = s;
}
// m_streamStartTime += (PaTime)framesPerBuffer / (PaTime)m_sampleRate;
return paContinue;
}

View File

@ -1,26 +1,26 @@
#include <QMessageBox>
#include <portaudio.h>
//#include <portaudio.h>
#include "audio/portaudioholder.h"
PortAudioHolder::PortAudioHolder() :
m_initialized(false)
{
PaError err;
//PaError err;
if((err = Pa_Initialize()) == paNoError) {
//if((err = Pa_Initialize()) == paNoError) {
m_initialized = true;
qDebug("PortAudio initialized");
} else {
qCritical("PortAudio: could not initialise: %s (%d)", Pa_GetErrorText(err), err);
QString error = QObject::tr("PortAudio could not be initialised: %1 (%2)").arg(Pa_GetErrorText(err)).arg(err);
QMessageBox::critical(NULL, "PortAudio failure", error);
}
//} else {
// qCritical("PortAudio: could not initialise: %s (%d)", Pa_GetErrorText(err), err);
// QString error = QObject::tr("PortAudio could not be initialised: %1 (%2)").arg(Pa_GetErrorText(err)).arg(err);
// QMessageBox::critical(NULL, "PortAudio failure", error);
//}
}
PortAudioHolder::~PortAudioHolder()
{
if(m_initialized) {
Pa_Terminate();
//Pa_Terminate();
qDebug("PortAudio terminated");
}
}