Audio output: forward actual sample rate to Audio Manager

This commit is contained in:
f4exb 2023-07-07 04:58:05 +02:00
parent 768d3f1398
commit 5df7f73da7
4 changed files with 98 additions and 40 deletions

View File

@ -96,6 +96,8 @@ AudioDeviceManager::AudioDeviceManager()
m_defaultInputStarted = false; m_defaultInputStarted = false;
m_defaultOutputStarted = false; m_defaultOutputStarted = false;
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection);
} }
AudioDeviceManager::~AudioDeviceManager() AudioDeviceManager::~AudioDeviceManager()
@ -281,6 +283,7 @@ void AudioDeviceManager::addAudioSink(AudioFifo* audioFifo, MessageQueue *sample
} }
qDebug("AudioDeviceManager::addAudioSink: new AudioOutputDevice on thread: %p", thread); qDebug("AudioDeviceManager::addAudioSink: new AudioOutputDevice on thread: %p", thread);
audioOutputDevice->setManagerMessageQueue(&m_inputMessageQueue);
audioOutputDevice->moveToThread(thread); audioOutputDevice->moveToThread(thread);
QObject::connect( QObject::connect(
@ -445,7 +448,6 @@ void AudioDeviceManager::startAudioOutput(int outputDeviceIndex)
AudioOutputDevice::MsgStart *msg = AudioOutputDevice::MsgStart::create(outputDeviceIndex, sampleRate); AudioOutputDevice::MsgStart *msg = AudioOutputDevice::MsgStart::create(outputDeviceIndex, sampleRate);
m_audioOutputs[outputDeviceIndex]->getInputMessageQueue()->push(msg); m_audioOutputs[outputDeviceIndex]->getInputMessageQueue()->push(msg);
m_audioOutputInfos[deviceName].sampleRate = sampleRate; // FIXME: possible change of sample rate in AudioOutputDevice
m_audioOutputInfos[deviceName].udpAddress = udpAddress; m_audioOutputInfos[deviceName].udpAddress = udpAddress;
m_audioOutputInfos[deviceName].udpPort = udpPort; m_audioOutputInfos[deviceName].udpPort = udpPort;
m_audioOutputInfos[deviceName].copyToUDP = copyAudioToUDP; m_audioOutputInfos[deviceName].copyToUDP = copyAudioToUDP;
@ -488,7 +490,7 @@ void AudioDeviceManager::startAudioInput(int inputDeviceIndex)
m_audioInputs[inputDeviceIndex]->start(inputDeviceIndex, sampleRate); m_audioInputs[inputDeviceIndex]->start(inputDeviceIndex, sampleRate);
m_audioInputs[inputDeviceIndex]->setVolume(volume); m_audioInputs[inputDeviceIndex]->setVolume(volume);
m_audioInputInfos[deviceName].sampleRate = m_audioInputs[inputDeviceIndex]->getRate(); m_audioInputInfos[deviceName].sampleRate = m_audioInputs[inputDeviceIndex]->getRate(); // FIXME
m_audioInputInfos[deviceName].volume = volume; m_audioInputInfos[deviceName].volume = volume;
m_defaultInputStarted = (inputDeviceIndex == -1); m_defaultInputStarted = (inputDeviceIndex == -1);
} }
@ -671,17 +673,6 @@ void AudioDeviceManager::setOutputDeviceInfo(int outputDeviceIndex, const Output
AudioOutputDevice::MsgStart *msgStart = AudioOutputDevice::MsgStart::create(outputDeviceIndex, deviceInfo.sampleRate); AudioOutputDevice::MsgStart *msgStart = AudioOutputDevice::MsgStart::create(outputDeviceIndex, deviceInfo.sampleRate);
audioOutput->getInputMessageQueue()->push(msgStart); audioOutput->getInputMessageQueue()->push(msgStart);
m_audioOutputInfos[deviceName].sampleRate = audioOutput->getRate(); // store actual sample rate
// send message to attached channels
QList<MessageQueue *>::const_iterator it = m_outputDeviceSinkMessageQueues[outputDeviceIndex].begin();
for (; it != m_outputDeviceSinkMessageQueues[outputDeviceIndex].end(); ++it)
{
DSPConfigureAudio *msg = new DSPConfigureAudio(m_audioOutputInfos[deviceName].sampleRate, DSPConfigureAudio::AudioOutput);
(*it)->push(msg);
}
} }
audioOutput->setUdpCopyToUDP(deviceInfo.copyToUDP); audioOutput->setUdpCopyToUDP(deviceInfo.copyToUDP);
@ -724,18 +715,6 @@ void AudioDeviceManager::unsetOutputDeviceInfo(int outputDeviceIndex)
stopAudioOutput(outputDeviceIndex); stopAudioOutput(outputDeviceIndex);
startAudioOutput(outputDeviceIndex); startAudioOutput(outputDeviceIndex);
if (oldDeviceInfo.sampleRate != m_audioOutputInfos[deviceName].sampleRate)
{
// send message to attached channels
QList<MessageQueue *>::const_iterator it = m_outputDeviceSinkMessageQueues[outputDeviceIndex].begin();
for (; it != m_outputDeviceSinkMessageQueues[outputDeviceIndex].end(); ++it)
{
DSPConfigureAudio *msg = new DSPConfigureAudio(m_audioOutputInfos[deviceName].sampleRate, DSPConfigureAudio::AudioOutput);
(*it)->push(msg);
}
}
} }
void AudioDeviceManager::unsetInputDeviceInfo(int inputDeviceIndex) void AudioDeviceManager::unsetInputDeviceInfo(int inputDeviceIndex)
@ -865,3 +844,40 @@ void AudioDeviceManager::debugAudioOutputInfos() const
<< " decimationFactor: " << it.value().udpDecimationFactor; << " decimationFactor: " << it.value().udpDecimationFactor;
} }
} }
bool AudioDeviceManager::handleMessage(const Message& msg)
{
if (AudioOutputDevice::MsgReportSampleRate::match(msg))
{
AudioOutputDevice::MsgReportSampleRate& report = (AudioOutputDevice::MsgReportSampleRate&) msg;
int deviceIndex = report.getDeviceIndex();
const QString& deviceName = report.getDeviceName();
int sampleRate = report.getSampleRate();
qDebug("AudioDeviceManager::handleMessage: AudioOutputDevice::MsgReportSampleRate: device(%d) %s: rate: %d",
deviceIndex, qPrintable(deviceName), sampleRate);
m_audioOutputInfos[deviceName].sampleRate = sampleRate;
// send message to attached channels
for (auto& messageQueue : m_outputDeviceSinkMessageQueues[deviceIndex])
{
DSPConfigureAudio *msg = new DSPConfigureAudio(m_audioOutputInfos[deviceName].sampleRate, DSPConfigureAudio::AudioOutput);
messageQueue->push(msg);
}
return true;
}
return false;
}
void AudioDeviceManager::handleInputMessages()
{
Message* message;
while ((message = m_inputMessageQueue.pop()) != nullptr)
{
if (handleMessage(*message)) {
delete message;
}
}
}

View File

@ -22,18 +22,20 @@
#include <QStringList> #include <QStringList>
#include <QList> #include <QList>
#include <QMap> #include <QMap>
#include <QObject>
#include "audio/audioinputdevice.h" #include "audio/audioinputdevice.h"
#include "audio/audiooutputdevice.h" #include "audio/audiooutputdevice.h"
#include "audio/audiodeviceinfo.h" #include "audio/audiodeviceinfo.h"
#include "util/messagequeue.h"
#include "export.h" #include "export.h"
class QThread; class QThread;
class QDataStream; class QDataStream;
class AudioFifo; class AudioFifo;
class MessageQueue;
class SDRBASE_API AudioDeviceManager { class SDRBASE_API AudioDeviceManager : public QObject {
Q_OBJECT
public: public:
class InputDeviceInfo class InputDeviceInfo
{ {
@ -149,6 +151,8 @@ private:
bool m_defaultOutputStarted; //!< True if the default audio output (-1) has already been started bool m_defaultOutputStarted; //!< True if the default audio output (-1) has already been started
bool m_defaultInputStarted; //!< True if the default audio input (-1) has already been started bool m_defaultInputStarted; //!< True if the default audio input (-1) has already been started
MessageQueue m_inputMessageQueue;
void resetToDefaults(); void resetToDefaults();
QByteArray serialize() const; QByteArray serialize() const;
bool deserialize(const QByteArray& data); bool deserialize(const QByteArray& data);
@ -166,7 +170,12 @@ private:
void deserializeOutputMap(QByteArray& data); void deserializeOutputMap(QByteArray& data);
void debugAudioOutputInfos() const; void debugAudioOutputInfos() const;
bool handleMessage(const Message& cmd);
friend class MainSettings; friend class MainSettings;
private slots:
void handleInputMessages();
}; };
QDataStream& operator<<(QDataStream& ds, const AudioDeviceManager::InputDeviceInfo& info); QDataStream& operator<<(QDataStream& ds, const AudioDeviceManager::InputDeviceInfo& info);

View File

@ -32,6 +32,7 @@
MESSAGE_CLASS_DEFINITION(AudioOutputDevice::MsgStart, Message) MESSAGE_CLASS_DEFINITION(AudioOutputDevice::MsgStart, Message)
MESSAGE_CLASS_DEFINITION(AudioOutputDevice::MsgStop, Message) MESSAGE_CLASS_DEFINITION(AudioOutputDevice::MsgStop, Message)
MESSAGE_CLASS_DEFINITION(AudioOutputDevice::MsgReportSampleRate, Message)
AudioOutputDevice::AudioOutputDevice() : AudioOutputDevice::AudioOutputDevice() :
m_audioOutput(nullptr), m_audioOutput(nullptr),
@ -47,7 +48,8 @@ AudioOutputDevice::AudioOutputDevice() :
m_recordSilenceTime(0), m_recordSilenceTime(0),
m_recordSilenceNbSamples(0), m_recordSilenceNbSamples(0),
m_recordSilenceCount(0), m_recordSilenceCount(0),
m_audioFifos() m_audioFifos(),
m_managerMessageQueue(nullptr)
{ {
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection);
} }
@ -66,18 +68,18 @@ AudioOutputDevice::~AudioOutputDevice()
// m_audioFifos.clear(); // m_audioFifos.clear();
} }
bool AudioOutputDevice::start(int device, int rate) bool AudioOutputDevice::start(int deviceIndex, int sampleRate)
{ {
// if (m_audioOutput) { // if (m_audioOutput) {
// return true; // return true;
// } // }
// if (m_audioUsageCount == 0) // if (m_audioUsageCount == 0)
// { // {
qDebug("AudioOutputDevice::start: device: %d rate: %d thread: %p", device, rate, QThread::currentThread()); qDebug("AudioOutputDevice::start: device: %d rate: %d thread: %p", deviceIndex, sampleRate, QThread::currentThread());
QMutexLocker mutexLocker(&m_mutex); QMutexLocker mutexLocker(&m_mutex);
AudioDeviceInfo devInfo; AudioDeviceInfo devInfo;
if (device < 0) if (deviceIndex < 0)
{ {
devInfo = AudioDeviceInfo::defaultOutputDevice(); devInfo = AudioDeviceInfo::defaultOutputDevice();
qWarning("AudioOutputDevice::start: using system default device %s", qPrintable(devInfo.defaultOutputDevice().deviceName())); qWarning("AudioOutputDevice::start: using system default device %s", qPrintable(devInfo.defaultOutputDevice().deviceName()));
@ -86,15 +88,16 @@ bool AudioOutputDevice::start(int device, int rate)
{ {
QList<AudioDeviceInfo> devicesInfo = AudioDeviceInfo::availableOutputDevices(); QList<AudioDeviceInfo> devicesInfo = AudioDeviceInfo::availableOutputDevices();
if (device < devicesInfo.size()) if (deviceIndex < devicesInfo.size())
{ {
devInfo = devicesInfo[device]; devInfo = devicesInfo[deviceIndex];
qWarning("AudioOutputDevice::start: using audio device #%d: %s", device, qPrintable(devInfo.deviceName())); qWarning("AudioOutputDevice::start: using audio device #%d: %s", deviceIndex, qPrintable(devInfo.deviceName()));
} }
else else
{ {
devInfo = AudioDeviceInfo::defaultOutputDevice(); devInfo = AudioDeviceInfo::defaultOutputDevice();
qWarning("AudioOutputDevice::start: audio device #%d does not exist. Using system default device %s", device, qPrintable(devInfo.defaultOutputDevice().deviceName())); qWarning("AudioOutputDevice::start: audio device #%d does not exist. Using system default device %s", deviceIndex, qPrintable(devInfo.defaultOutputDevice().deviceName()));
deviceIndex = -1;
} }
} }
@ -104,7 +107,7 @@ bool AudioOutputDevice::start(int device, int rate)
m_audioFormat = devInfo.deviceInfo().preferredFormat(); m_audioFormat = devInfo.deviceInfo().preferredFormat();
#endif #endif
m_audioFormat.setSampleRate(rate); m_audioFormat.setSampleRate(sampleRate);
m_audioFormat.setChannelCount(2); m_audioFormat.setChannelCount(2);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
m_audioFormat.setSampleFormat(QAudioFormat::Int16); m_audioFormat.setSampleFormat(QAudioFormat::Int16);
@ -118,7 +121,7 @@ bool AudioOutputDevice::start(int device, int rate)
if (!devInfo.isFormatSupported(m_audioFormat)) if (!devInfo.isFormatSupported(m_audioFormat))
{ {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
qWarning("AudioOutputDevice::start: format %d Hz 2xS16LE audio/pcm not supported.", rate); qWarning("AudioOutputDevice::start: format %d Hz 2xS16LE audio/pcm not supported.", sampleRate);
#else #else
m_audioFormat = devInfo.deviceInfo().nearestFormat(m_audioFormat); m_audioFormat = devInfo.deviceInfo().nearestFormat(m_audioFormat);
std::ostringstream os; std::ostringstream os;
@ -128,7 +131,7 @@ bool AudioOutputDevice::start(int device, int rate)
<< " codec: " << m_audioFormat.codec().toStdString() << " codec: " << m_audioFormat.codec().toStdString()
<< " byteOrder: " << (m_audioFormat.byteOrder() == QAudioFormat::BigEndian ? "BE" : "LE") << " byteOrder: " << (m_audioFormat.byteOrder() == QAudioFormat::BigEndian ? "BE" : "LE")
<< " sampleType: " << (int) m_audioFormat.sampleType(); << " sampleType: " << (int) m_audioFormat.sampleType();
qWarning("AudioOutputDevice::start: format %d Hz 2xS16LE audio/pcm not supported. Using: %s", rate, os.str().c_str()); qWarning("AudioOutputDevice::start: format %d Hz 2xS16LE audio/pcm not supported. Using: %s", sampleRate, os.str().c_str());
#endif #endif
} }
else else
@ -161,7 +164,7 @@ bool AudioOutputDevice::start(int device, int rate)
// m_audioOutput->setBufferSize(m_audioFormat.sampleRate() / 5); FIXME: does not work generally // m_audioOutput->setBufferSize(m_audioFormat.sampleRate() / 5); FIXME: does not work generally
m_recordSilenceNbSamples = (m_recordSilenceTime * m_audioFormat.sampleRate()) / 10; // time in 100'ś ms m_recordSilenceNbSamples = (m_recordSilenceTime * m_audioFormat.sampleRate()) / 10; // time in 100'ś ms
QIODevice::open(QIODevice::ReadOnly); QIODevice::open(QIODevice::ReadOnly | QIODevice::Unbuffered);
m_audioOutput->start(this); m_audioOutput->start(this);
@ -170,6 +173,10 @@ bool AudioOutputDevice::start(int device, int rate)
} else { } else {
qDebug("AudioOutputDevice::start: started buffer: %d bytes", m_audioOutput->bufferSize()); qDebug("AudioOutputDevice::start: started buffer: %d bytes", m_audioOutput->bufferSize());
} }
if (m_managerMessageQueue) {
m_managerMessageQueue->push(AudioOutputDevice::MsgReportSampleRate::create(deviceIndex, devInfo.deviceName(), m_audioFormat.sampleRate()));
}
// } // }
// //
// m_audioUsageCount++; // m_audioUsageCount++;

View File

@ -77,6 +77,30 @@ public:
{ } { }
}; };
class MsgReportSampleRate : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getDeviceIndex() const { return m_deviceIndex; }
const QString& getDeviceName() const { return m_deviceName; }
int getSampleRate() const { return m_sampleRate; }
static MsgReportSampleRate* create(int deviceIndex, const QString& deviceName, int sampleRate) {
return new MsgReportSampleRate(deviceIndex, deviceName, sampleRate);
}
private:
int m_deviceIndex;
QString m_deviceName;
int m_sampleRate;
MsgReportSampleRate(int deviceIndex, const QString& deviceName, int sampleRate) :
Message(),
m_deviceIndex(deviceIndex),
m_deviceName(deviceName),
m_sampleRate(sampleRate)
{ }
};
enum UDPChannelMode enum UDPChannelMode
{ {
UDPChannelLeft, UDPChannelLeft,
@ -118,6 +142,7 @@ public:
void setDeviceName(const QString& deviceName) { m_deviceName = deviceName;} void setDeviceName(const QString& deviceName) { m_deviceName = deviceName;}
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
void setManagerMessageQueue(MessageQueue *messageQueue) { m_managerMessageQueue = messageQueue; }
private: private:
QRecursiveMutex m_mutex; QRecursiveMutex m_mutex;
@ -147,6 +172,7 @@ private:
QString m_deviceName; QString m_deviceName;
MessageQueue m_inputMessageQueue; MessageQueue m_inputMessageQueue;
MessageQueue *m_managerMessageQueue;
//virtual bool open(OpenMode mode); //virtual bool open(OpenMode mode);
virtual qint64 readData(char* data, qint64 maxLen); virtual qint64 readData(char* data, qint64 maxLen);
@ -155,7 +181,7 @@ private:
void writeSampleToFile(qint16 lSample, qint16 rSample); void writeSampleToFile(qint16 lSample, qint16 rSample);
bool handleMessage(const Message& cmd); bool handleMessage(const Message& cmd);
bool start(int device, int rate); bool start(int deviceIndex, int sampleRate);
void stop(); void stop();
friend class AudioOutputPipe; friend class AudioOutputPipe;