kopia lustrzana https://github.com/f4exb/sdrangel
Audio output device recording: implementation (1)
rodzic
e89331f58f
commit
4a383e439b
|
@ -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));
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Ładowanie…
Reference in New Issue