parent
99466ed1d9
commit
fca9f8870f
|
@ -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)
|
||||
|
||||
##############################################################################
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -44,4 +44,4 @@ target_link_libraries(demodnfm
|
|||
sdrbase
|
||||
)
|
||||
|
||||
qt5_use_modules(demodnfm Core Widgets OpenGL)
|
||||
qt5_use_modules(demodnfm Core Widgets OpenGL Multimedia)
|
|
@ -44,4 +44,4 @@ target_link_libraries(demodtetra
|
|||
sdrbase
|
||||
)
|
||||
|
||||
qt5_use_modules(demodtetra Core Widgets OpenGL)
|
||||
qt5_use_modules(demodtetra Core Widgets OpenGL Multimedia)
|
||||
|
|
|
@ -47,4 +47,4 @@ target_link_libraries(inputosmosdr
|
|||
sdrbase
|
||||
)
|
||||
|
||||
qt5_use_modules(inputosmosdr Core Widgets OpenGL)
|
||||
qt5_use_modules(inputosmosdr Core Widgets OpenGL Multimedia)
|
||||
|
|
|
@ -47,4 +47,4 @@ target_link_libraries(inputrtlsdr
|
|||
sdrbase
|
||||
)
|
||||
|
||||
qt5_use_modules(inputrtlsdr Core Widgets OpenGL)
|
||||
qt5_use_modules(inputrtlsdr Core Widgets OpenGL Multimedia)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue