1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-25 17:28:50 -05:00

Audio output device recording: implementation (1)

This commit is contained in:
f4exb 2022-11-11 20:53:50 +01:00
parent e89331f58f
commit 4a383e439b
5 changed files with 130 additions and 8 deletions

View File

@ -641,6 +641,9 @@ void AudioDeviceManager::setOutputDeviceInfo(int outputDeviceIndex, const Output
audioOutput->setUdpChannelMode(deviceInfo.udpChannelMode); audioOutput->setUdpChannelMode(deviceInfo.udpChannelMode);
audioOutput->setUdpChannelFormat(deviceInfo.udpChannelCodec, deviceInfo.udpChannelMode == AudioOutputDevice::UDPChannelStereo, deviceInfo.sampleRate); audioOutput->setUdpChannelFormat(deviceInfo.udpChannelCodec, deviceInfo.udpChannelMode == AudioOutputDevice::UDPChannelStereo, deviceInfo.sampleRate);
audioOutput->setUdpDecimation(deviceInfo.udpDecimationFactor); audioOutput->setUdpDecimation(deviceInfo.udpDecimationFactor);
audioOutput->setFileRecordName(deviceInfo.fileRecordName);
audioOutput->setRecordToFile(deviceInfo.recordToFile);
audioOutput->setRecordSilenceTime(deviceInfo.recordSilenceTime);
qDebug("AudioDeviceManager::setOutputDeviceInfo: index: %d device: %s updated", qDebug("AudioDeviceManager::setOutputDeviceInfo: index: %d device: %s updated",
outputDeviceIndex, qPrintable(deviceName)); outputDeviceIndex, qPrintable(deviceName));

View File

@ -23,16 +23,22 @@
#include "audiooutputdevice.h" #include "audiooutputdevice.h"
#include "audiofifo.h" #include "audiofifo.h"
#include "audionetsink.h" #include "audionetsink.h"
#include "dsp/wavfilerecord.h"
AudioOutputDevice::AudioOutputDevice() : AudioOutputDevice::AudioOutputDevice() :
m_audioOutput(0), m_audioOutput(nullptr),
m_audioNetSink(0), m_audioNetSink(nullptr),
m_copyAudioToUdp(false), m_wavFileRecord(nullptr),
m_copyAudioToUdp(false),
m_udpChannelMode(UDPChannelLeft), m_udpChannelMode(UDPChannelLeft),
m_udpChannelCodec(UDPCodecL16), m_udpChannelCodec(UDPCodecL16),
m_audioUsageCount(0), m_audioUsageCount(0),
m_onExit(false), m_onExit(false),
m_volume(1.0), m_volume(1.0),
m_recordToFile(false),
m_recordSilenceTime(0),
m_recordSilenceNbSamples(0),
m_recordSilenceCount(0),
m_audioFifos() m_audioFifos()
{ {
} }
@ -114,14 +120,15 @@ bool AudioOutputDevice::start(int device, int rate)
m_audioOutput = new QAudioOutput(devInfo, m_audioFormat); m_audioOutput = new QAudioOutput(devInfo, m_audioFormat);
m_audioNetSink = new AudioNetSink(0, m_audioFormat.sampleRate(), false); m_audioNetSink = new AudioNetSink(0, m_audioFormat.sampleRate(), false);
m_wavFileRecord = new WavFileRecord(m_audioFormat.sampleRate());
m_audioOutput->setVolume(m_volume); m_audioOutput->setVolume(m_volume);
m_recordSilenceNbSamples = (m_recordSilenceTime * m_audioFormat.sampleRate()) / 10; // time in 100'ś ms
QIODevice::open(QIODevice::ReadOnly); QIODevice::open(QIODevice::ReadOnly);
m_audioOutput->start(this); m_audioOutput->start(this);
if (m_audioOutput->state() != QAudio::ActiveState) if (m_audioOutput->state() != QAudio::ActiveState) {
{
qWarning("AudioOutputDevice::start: cannot start"); qWarning("AudioOutputDevice::start: cannot start");
} }
// } // }
@ -139,8 +146,11 @@ void AudioOutputDevice::stop()
m_audioOutput->stop(); m_audioOutput->stop();
QIODevice::close(); QIODevice::close();
delete m_audioNetSink; delete m_audioNetSink;
m_audioNetSink = 0; m_audioNetSink = nullptr;
delete m_wavFileRecord;
m_wavFileRecord = nullptr;
delete m_audioOutput; delete m_audioOutput;
m_audioOutput = nullptr;
// if (m_audioUsageCount > 0) // if (m_audioUsageCount > 0)
// { // {
@ -161,14 +171,12 @@ void AudioOutputDevice::stop()
void AudioOutputDevice::addFifo(AudioFifo* audioFifo) void AudioOutputDevice::addFifo(AudioFifo* audioFifo)
{ {
QMutexLocker mutexLocker(&m_mutex); QMutexLocker mutexLocker(&m_mutex);
m_audioFifos.push_back(audioFifo); m_audioFifos.push_back(audioFifo);
} }
void AudioOutputDevice::removeFifo(AudioFifo* audioFifo) void AudioOutputDevice::removeFifo(AudioFifo* audioFifo)
{ {
QMutexLocker mutexLocker(&m_mutex); QMutexLocker mutexLocker(&m_mutex);
m_audioFifos.remove(audioFifo); m_audioFifos.remove(audioFifo);
} }
@ -219,6 +227,63 @@ void AudioOutputDevice::setUdpDecimation(uint32_t decimation)
} }
} }
void AudioOutputDevice::setFileRecordName(const QString& fileRecordName)
{
if (!m_wavFileRecord) {
return;
}
QStringList dotBreakout = fileRecordName.split(QLatin1Char('.'));
if (dotBreakout.size() > 1) {
QString extension = dotBreakout.last();
if (extension != "wav") {
dotBreakout.last() = "wav";
}
}
else
{
dotBreakout.append("wav");
}
QString newFileRecordName = dotBreakout.join(QLatin1Char('.'));
QString fileBase;
FileRecordInterface::guessTypeFromFileName(newFileRecordName, fileBase);
qDebug("AudioOutputDevice::setFileRecordName: newFileRecordName: %s fileBase: %s", qPrintable(newFileRecordName), qPrintable(fileBase));
m_wavFileRecord->setFileName(fileBase);
}
void AudioOutputDevice::setRecordToFile(bool recordToFile)
{
if (!m_wavFileRecord) {
return;
}
if (recordToFile)
{
if (!m_wavFileRecord->isRecording()) {
m_wavFileRecord->startRecording();
}
}
else
{
if (m_wavFileRecord->isRecording()) {
m_wavFileRecord->stopRecording();
}
}
m_recordToFile = recordToFile;
m_recordSilenceCount = 0;
}
void AudioOutputDevice::setRecordSilenceTime(int recordSilenceTime)
{
m_recordSilenceNbSamples = (recordSilenceTime * m_audioFormat.sampleRate()) / 10; // time in 100'ś ms
m_recordSilenceCount = 0;
m_recordSilenceTime = recordSilenceTime;
}
qint64 AudioOutputDevice::readData(char* data, qint64 maxLen) qint64 AudioOutputDevice::readData(char* data, qint64 maxLen)
{ {
//qDebug("AudioOutputDevice::readData: %lld", maxLen); //qDebug("AudioOutputDevice::readData: %lld", maxLen);
@ -331,6 +396,36 @@ qint64 AudioOutputDevice::readData(char* data, qint64 maxLen)
break; break;
} }
} }
if ((m_recordToFile) && (m_wavFileRecord))
{
if ((sr == 0) && (sl == 0))
{
if (m_recordSilenceNbSamples <= 0)
{
m_wavFileRecord->write(sl, sr);
m_recordSilenceCount = 0;
}
else if (m_recordSilenceCount < m_recordSilenceNbSamples)
{
m_wavFileRecord->write(sl, sr);
m_recordSilenceCount++;
}
else
{
m_wavFileRecord->stopRecording();
}
}
else
{
if (!m_wavFileRecord->isRecording()) {
m_wavFileRecord->startRecording();
}
m_wavFileRecord->write(sl, sr);
m_recordSilenceCount = 0;
}
}
} }
return samplesPerBuffer * 4; return samplesPerBuffer * 4;

View File

@ -31,6 +31,7 @@ class QAudioOutput;
class AudioFifo; class AudioFifo;
class AudioOutputPipe; class AudioOutputPipe;
class AudioNetSink; class AudioNetSink;
class WavFileRecord;
class SDRBASE_API AudioOutputDevice : QIODevice { class SDRBASE_API AudioOutputDevice : QIODevice {
public: public:
@ -72,17 +73,26 @@ public:
void setUdpChannelFormat(UDPChannelCodec udpChannelCodec, bool stereo, int sampleRate); void setUdpChannelFormat(UDPChannelCodec udpChannelCodec, bool stereo, int sampleRate);
void setUdpDecimation(uint32_t decimation); void setUdpDecimation(uint32_t decimation);
void setVolume(float volume); void setVolume(float volume);
void setFileRecordName(const QString& fileRecordName);
void setRecordToFile(bool recordToFile);
void setRecordSilenceTime(int recordSilenceTime);
private: private:
QRecursiveMutex m_mutex; QRecursiveMutex m_mutex;
QAudioOutput* m_audioOutput; QAudioOutput* m_audioOutput;
AudioNetSink* m_audioNetSink; AudioNetSink* m_audioNetSink;
WavFileRecord* m_wavFileRecord;
bool m_copyAudioToUdp; bool m_copyAudioToUdp;
UDPChannelMode m_udpChannelMode; UDPChannelMode m_udpChannelMode;
UDPChannelCodec m_udpChannelCodec; UDPChannelCodec m_udpChannelCodec;
uint m_audioUsageCount; uint m_audioUsageCount;
bool m_onExit; bool m_onExit;
float m_volume; float m_volume;
QString m_fileRecordName;
bool m_recordToFile;
int m_recordSilenceTime;
int m_recordSilenceNbSamples;
int m_recordSilenceCount;
std::list<AudioFifo*> m_audioFifos; std::list<AudioFifo*> m_audioFifos;
std::vector<qint32> m_mixBuffer; std::vector<qint32> m_mixBuffer;

View File

@ -111,6 +111,19 @@ void WavFileRecord::feed(const SampleVector::const_iterator& begin, const Sample
} }
} }
void WavFileRecord::write(qint16 lSample, qint16 rSample)
{
if (m_recordStart)
{
writeHeader();
m_recordStart = false;
}
m_sampleFile.write(reinterpret_cast<const char*>(&lSample), 2);
m_sampleFile.write(reinterpret_cast<const char*>(&rSample), 2);
m_byteCount += 4;
}
void WavFileRecord::start() void WavFileRecord::start()
{ {
} }

View File

@ -97,6 +97,7 @@ public:
void genUniqueFileName(uint deviceUID, int istream = -1); void genUniqueFileName(uint deviceUID, int istream = -1);
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly) override; virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly) override;
void write(qint16 lSample, qint16 rSample); //!< write a single sample
virtual void start() override; virtual void start() override;
virtual void stop() override; virtual void stop() override;
virtual bool handleMessage(const Message& message) override; virtual bool handleMessage(const Message& message) override;