From 4c3db01409ccb67465c0cfce5d41776f8f1dcb52 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 28 Nov 2016 18:32:50 +0100 Subject: [PATCH] Added an AudioInput class --- CMakeLists.txt | 2 + sdrbase/audio/audioinput.cpp | 183 +++++++++++++++++++++++++++++++++++ sdrbase/audio/audioinput.h | 67 +++++++++++++ sdrbase/sdrbase.pro | 2 + 4 files changed, 254 insertions(+) create mode 100644 sdrbase/audio/audioinput.cpp create mode 100644 sdrbase/audio/audioinput.h diff --git a/CMakeLists.txt b/CMakeLists.txt index db8215811..c7639a793 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,6 +94,7 @@ set(sdrbase_SOURCES sdrbase/audio/audiodeviceinfo.cpp sdrbase/audio/audiofifo.cpp sdrbase/audio/audiooutput.cpp + sdrbase/audio/audioinput.cpp sdrbase/dsp/afsquelch.cpp sdrbase/dsp/agc.cpp @@ -185,6 +186,7 @@ set(sdrbase_HEADERS sdrbase/audio/audiodeviceinfo.h sdrbase/audio/audiofifo.h sdrbase/audio/audiooutput.h + sdrbase/audio/audioinput.h sdrbase/dsp/afsquelch.h sdrbase/dsp/downchannelizer.h diff --git a/sdrbase/audio/audioinput.cpp b/sdrbase/audio/audioinput.cpp new file mode 100644 index 000000000..2476d779a --- /dev/null +++ b/sdrbase/audio/audioinput.cpp @@ -0,0 +1,183 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include "audio/audioinput.h" +#include "audio/audiofifo.h" + +AudioInput::AudioInput() : + m_mutex(), + m_audioInput(0), + m_audioUsageCount(0), + m_onExit(false), + m_audioFifos() +{ +} + +AudioInput::~AudioInput() +{ + stop(); + + QMutexLocker mutexLocker(&m_mutex); + + for (AudioFifos::iterator it = m_audioFifos.begin(); it != m_audioFifos.end(); ++it) + { + delete *it; + } + + m_audioFifos.clear(); +} + +bool AudioInput::start(int device, int rate) +{ + QMutexLocker mutexLocker(&m_mutex); + + if (m_audioUsageCount == 0) + { + QAudioDeviceInfo devInfo; + + if (device < 0) + { + devInfo = QAudioDeviceInfo::defaultInputDevice(); + qWarning("AudioInput::start: using default device %s", qPrintable(devInfo.defaultInputDevice().deviceName())); + } + else + { + QList devicesInfo = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); + + if (device < devicesInfo.size()) + { + devInfo = devicesInfo[device]; + qWarning("AudioInput::start: using audio device #%d: %s", device, qPrintable(devInfo.defaultInputDevice().deviceName())); + } + else + { + devInfo = QAudioDeviceInfo::defaultInputDevice(); + qWarning("AudioInput::start: audio device #%d does not exist. Using default device %s", device, qPrintable(devInfo.defaultInputDevice().deviceName())); + } + } + + //QAudioDeviceInfo devInfo(QAudioDeviceInfo::defaultOutputDevice()); + + m_audioFormat.setSampleRate(rate); + m_audioFormat.setChannelCount(2); + m_audioFormat.setSampleSize(16); + m_audioFormat.setCodec("audio/pcm"); + m_audioFormat.setByteOrder(QAudioFormat::LittleEndian); + m_audioFormat.setSampleType(QAudioFormat::SignedInt); + + if (!devInfo.isFormatSupported(m_audioFormat)) + { + m_audioFormat = devInfo.nearestFormat(m_audioFormat); + qWarning("AudioInput::start: %d Hz S16_LE audio format not supported. New rate: %d", rate, m_audioFormat.sampleRate()); + } + + if (m_audioFormat.sampleSize() != 16) + { + qWarning("AudioInput::start: Audio device ( %s ) failed", qPrintable(devInfo.defaultInputDevice().deviceName())); + return false; + } + + m_audioInput = new QAudioInput(devInfo, m_audioFormat); + + QIODevice::open(QIODevice::ReadOnly); + + m_audioInput->start(this); + + if (m_audioInput->state() != QAudio::ActiveState) + { + qWarning("AudioInput::start: cannot start"); + } + } + + m_audioUsageCount++; + + return true; +} + +void AudioInput::stop() +{ + qDebug("AudioInput::stop"); + + QMutexLocker mutexLocker(&m_mutex); + + if (m_audioUsageCount > 0) + { + m_audioUsageCount--; + + if (m_audioUsageCount == 0) + { + QIODevice::close(); + + if (!m_onExit) { + delete m_audioInput; + } + } + } +} + +void AudioInput::addFifo(AudioFifo* audioFifo) +{ + QMutexLocker mutexLocker(&m_mutex); + + m_audioFifos.push_back(audioFifo); +} + +void AudioInput::removeFifo(AudioFifo* audioFifo) +{ + QMutexLocker mutexLocker(&m_mutex); + + m_audioFifos.remove(audioFifo); +} + +qint64 AudioInput::readData(char* data, qint64 maxLen) +{ + Q_UNUSED(data); + Q_UNUSED(maxLen); + return 0; +} + +qint64 AudioInput::writeData(const char *data, qint64 len) +{ + // @TODO: Study this mutex on OSX, for now deadlocks possible +#ifndef __APPLE__ + QMutexLocker mutexLocker(&m_mutex); +#endif + + if ((m_audioFormat.sampleSize() != 16) + || (m_audioFormat.sampleType() != QAudioFormat::SignedInt) + || (m_audioFormat.byteOrder() != QAudioFormat::LittleEndian)) + { + qCritical("AudioInput::writeData: invalid format not S16LE"); + return 0; + } + + if (m_audioFormat.channelCount() != 2) { + qCritical("AudioInput::writeData: invalid format not stereo"); + return 0; + } + + for (AudioFifos::iterator it = m_audioFifos.begin(); it != m_audioFifos.end(); ++it) + { + (*it)->write(reinterpret_cast(data), len/4, 10); + } + + return len; +} + diff --git a/sdrbase/audio/audioinput.h b/sdrbase/audio/audioinput.h new file mode 100644 index 000000000..196ae0459 --- /dev/null +++ b/sdrbase/audio/audioinput.h @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRBASE_AUDIO_AUDIOINPUT_H_ +#define SDRBASE_AUDIO_AUDIOINPUT_H_ + +#include +#include +#include +#include +#include +#include "util/export.h" + +class QAudioInput; +class AudioFifo; +class AudioOutputPipe; + + +class SDRANGEL_API AudioInput : public QIODevice { +public: + AudioInput(); + virtual ~AudioInput(); + + bool start(int device, int rate); + void stop(); + + void addFifo(AudioFifo* audioFifo); + void removeFifo(AudioFifo* audioFifo); + + uint getRate() const { return m_audioFormat.sampleRate(); } + void setOnExit(bool onExit) { m_onExit = onExit; } + +private: + QMutex m_mutex; + QAudioInput* m_audioInput; + uint m_audioUsageCount; + bool m_onExit; + + typedef std::list AudioFifos; + AudioFifos m_audioFifos; + std::vector m_mixBuffer; + + QAudioFormat m_audioFormat; + + //virtual bool open(OpenMode mode); + virtual qint64 readData(char* data, qint64 maxLen); + virtual qint64 writeData(const char* data, qint64 len); + + friend class AudioOutputPipe; +}; + + + +#endif /* SDRBASE_AUDIO_AUDIOINPUT_H_ */ diff --git a/sdrbase/sdrbase.pro b/sdrbase/sdrbase.pro index 10bce8e2c..b62db5bce 100644 --- a/sdrbase/sdrbase.pro +++ b/sdrbase/sdrbase.pro @@ -34,6 +34,7 @@ SOURCES += mainwindow.cpp\ audio/audiodeviceinfo.cpp\ audio/audiofifo.cpp\ audio/audiooutput.cpp\ + audio/audioinput.cpp\ device/devicesourceapi.cpp\ device/devicesinkapi.cpp\ dsp/afsquelch.cpp\ @@ -116,6 +117,7 @@ HEADERS += mainwindow.h\ audio/audiodeviceinfo.h\ audio/audiofifo.h\ audio/audiooutput.h\ + audio/audioinput.h\ device/devicesourceapi.h\ device/devicesinkapi.h\ dsp/afsquelch.h\