mirror of
https://github.com/cjcliffe/CubicSDR.git
synced 2024-11-23 04:08:36 -05:00
initial audio sink file and thread handling rough-in
This commit is contained in:
parent
21dd062da0
commit
c202d99a2a
@ -348,6 +348,9 @@ SET (cubicsdr_sources
|
|||||||
src/modules/modem/analog/ModemUSB.cpp
|
src/modules/modem/analog/ModemUSB.cpp
|
||||||
src/audio/AudioThread.cpp
|
src/audio/AudioThread.cpp
|
||||||
src/audio/AudioSinkThread.cpp
|
src/audio/AudioSinkThread.cpp
|
||||||
|
src/audio/AudioSinkFileThread.cpp
|
||||||
|
src/audio/AudioFile.cpp
|
||||||
|
src/audio/AudioFileWAV.cpp
|
||||||
src/util/Gradient.cpp
|
src/util/Gradient.cpp
|
||||||
src/util/Timer.cpp
|
src/util/Timer.cpp
|
||||||
src/util/MouseTracker.cpp
|
src/util/MouseTracker.cpp
|
||||||
@ -453,6 +456,9 @@ SET (cubicsdr_headers
|
|||||||
src/modules/modem/analog/ModemUSB.h
|
src/modules/modem/analog/ModemUSB.h
|
||||||
src/audio/AudioThread.h
|
src/audio/AudioThread.h
|
||||||
src/audio/AudioSinkThread.h
|
src/audio/AudioSinkThread.h
|
||||||
|
src/audio/AudioSinkFileThread.h
|
||||||
|
src/audio/AudioFile.h
|
||||||
|
src/audio/AudioFileWAV.h
|
||||||
src/util/Gradient.h
|
src/util/Gradient.h
|
||||||
src/util/Timer.h
|
src/util/Timer.h
|
||||||
src/util/ThreadBlockingQueue.h
|
src/util/ThreadBlockingQueue.h
|
||||||
|
27
src/audio/AudioFile.cpp
Normal file
27
src/audio/AudioFile.cpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Copyright (c) Charles J. Cliffe
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
|
||||||
|
#include "AudioFile.h"
|
||||||
|
#include "CubicSDR.h"
|
||||||
|
|
||||||
|
AudioFile::AudioFile() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioFile::~AudioFile() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioFile::setOutputFileName(std::string filename) {
|
||||||
|
filenameBase = filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AudioFile::getOutputFileName() {
|
||||||
|
std::string recPath = wxGetApp().getConfig()->getRecordingPath();
|
||||||
|
|
||||||
|
// TODO: Handle invalid chars, etc..
|
||||||
|
std::string filenameBaseSafe = filenameBase;
|
||||||
|
|
||||||
|
return recPath + filePathSeparator + filenameBaseSafe + getSuffix() + "." + getExtension();
|
||||||
|
}
|
||||||
|
|
26
src/audio/AudioFile.h
Normal file
26
src/audio/AudioFile.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) Charles J. Cliffe
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "AudioThread.h"
|
||||||
|
|
||||||
|
class AudioFile
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
AudioFile();
|
||||||
|
virtual ~AudioFile();
|
||||||
|
|
||||||
|
virtual void setOutputFileName(std::string filename);
|
||||||
|
virtual std::string getExtension() = 0;
|
||||||
|
virtual std::string getSuffix() = 0;
|
||||||
|
virtual std::string getOutputFileName();
|
||||||
|
|
||||||
|
virtual bool writeToFile(AudioThreadInputPtr input) = 0;
|
||||||
|
virtual bool closeFile() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string filenameBase;
|
||||||
|
|
||||||
|
};
|
47
src/audio/AudioFileWAV.cpp
Normal file
47
src/audio/AudioFileWAV.cpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (c) Charles J. Cliffe
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
|
||||||
|
#include "AudioFileWAV.h"
|
||||||
|
// #include "WavFileFormatHandlerStuff.h"
|
||||||
|
|
||||||
|
AudioFileWAV::AudioFileWAV() : AudioFile() {
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioFileWAV::~AudioFileWAV() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string AudioFileWAV::getExtension()
|
||||||
|
{
|
||||||
|
return "wav";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AudioFileWAV::getSuffix()
|
||||||
|
{
|
||||||
|
return suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioFileWAV::writeToFile(AudioThreadInputPtr input)
|
||||||
|
{
|
||||||
|
if (!outputFileStream.is_open()) {
|
||||||
|
suffix = "";
|
||||||
|
std::string ofName = getOutputFileName();
|
||||||
|
|
||||||
|
// Check if file exists, sequence the suffix?
|
||||||
|
|
||||||
|
outputFileStream.open(ofName.c_str(), std::ios::out | std::ios::binary);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write input data to wav file
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioFileWAV::closeFile()
|
||||||
|
{
|
||||||
|
if (outputFileStream.is_open()) {
|
||||||
|
outputFileStream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
26
src/audio/AudioFileWAV.h
Normal file
26
src/audio/AudioFileWAV.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) Charles J. Cliffe
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "AudioFile.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
class AudioFileWAV : public AudioFile {
|
||||||
|
|
||||||
|
public:
|
||||||
|
AudioFileWAV();
|
||||||
|
~AudioFileWAV();
|
||||||
|
|
||||||
|
std::string getExtension();
|
||||||
|
std::string getSuffix();
|
||||||
|
|
||||||
|
bool writeToFile(AudioThreadInputPtr input);
|
||||||
|
bool closeFile();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::ofstream outputFileStream;
|
||||||
|
std::string suffix;
|
||||||
|
|
||||||
|
};
|
34
src/audio/AudioSinkFileThread.cpp
Normal file
34
src/audio/AudioSinkFileThread.cpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (c) Charles J. Cliffe
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
|
||||||
|
#include "AudioSinkFileThread.h"
|
||||||
|
|
||||||
|
AudioSinkFileThread::AudioSinkFileThread() : AudioSinkThread() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioSinkFileThread::~AudioSinkFileThread() {
|
||||||
|
if (outputFileHandler != nullptr) {
|
||||||
|
outputFileHandler->closeFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioSinkFileThread::sink(AudioThreadInputPtr input) {
|
||||||
|
if (!outputFileHandler) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// forward to output file handler
|
||||||
|
outputFileHandler->writeToFile(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioSinkFileThread::inputChanged(AudioThreadInput oldProps, AudioThreadInputPtr newProps) {
|
||||||
|
// close, set new parameters, adjust file name sequence and re-open?
|
||||||
|
if (!outputFileHandler) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioSinkFileThread::setOutput(AudioFile * output) {
|
||||||
|
outputFileHandler = output;
|
||||||
|
outputFileHandler->setOutputFileName(sinkName);
|
||||||
|
}
|
24
src/audio/AudioSinkFileThread.h
Normal file
24
src/audio/AudioSinkFileThread.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// Copyright (c) Charles J. Cliffe
|
||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "AudioSinkThread.h"
|
||||||
|
#include "AudioFile.h"
|
||||||
|
|
||||||
|
class AudioSinkFileThread : public AudioSinkThread {
|
||||||
|
|
||||||
|
public:
|
||||||
|
AudioSinkFileThread();
|
||||||
|
~AudioSinkFileThread();
|
||||||
|
|
||||||
|
void sink(AudioThreadInputPtr input);
|
||||||
|
void inputChanged(AudioThreadInput oldProps, AudioThreadInputPtr newProps);
|
||||||
|
|
||||||
|
void setOutput(AudioFile *output);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AudioFile *outputFileHandler = nullptr;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
@ -5,19 +5,16 @@
|
|||||||
|
|
||||||
#define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000)
|
#define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000)
|
||||||
|
|
||||||
AudioSinkThread::AudioSinkThread()
|
AudioSinkThread::AudioSinkThread() {
|
||||||
{
|
|
||||||
inputQueuePtr = std::make_shared<AudioThreadInputQueue>();
|
inputQueuePtr = std::make_shared<AudioThreadInputQueue>();
|
||||||
setInputQueue("input", inputQueuePtr);
|
setInputQueue("input", inputQueuePtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioSinkThread::~AudioSinkThread()
|
AudioSinkThread::~AudioSinkThread() {
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioSinkThread::run()
|
void AudioSinkThread::run() {
|
||||||
{
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
pthread_t tID = pthread_self(); // ID of this thread
|
pthread_t tID = pthread_self(); // ID of this thread
|
||||||
int priority = sched_get_priority_max(SCHED_RR) - 1;
|
int priority = sched_get_priority_max(SCHED_RR) - 1;
|
||||||
@ -46,38 +43,18 @@ void AudioSinkThread::run()
|
|||||||
inputRef.sampleRate = inp->sampleRate;
|
inputRef.sampleRate = inp->sampleRate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Thread termination, prevent fancy things to happen, lock the whole thing:
|
void AudioSinkThread::terminate() {
|
||||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
IOThread::terminate();
|
||||||
|
|
||||||
// Drain any remaining inputs, with a non-blocking pop
|
|
||||||
inputQueuePtr->flush();
|
inputQueuePtr->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioSinkThread::terminate()
|
void AudioSinkThread::setSinkName(std::string sinkName_in) {
|
||||||
{
|
|
||||||
IOThread::terminate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioSinkThread::sink(AudioThreadInputPtr * input)
|
|
||||||
{
|
|
||||||
// do something with the audio data
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioSinkThread::inputChanged(AudioThreadInput oldProps, AudioThreadInputPtr newProps)
|
|
||||||
{
|
|
||||||
// handle changes in stream properties
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioSinkThread::setSinkName(std::string sinkName_in)
|
|
||||||
{
|
|
||||||
sinkName = sinkName_in;
|
sinkName = sinkName_in;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string AudioSinkThread::getSinkName()
|
std::string AudioSinkThread::getSinkName() {
|
||||||
{
|
|
||||||
return sinkName;
|
return sinkName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,8 +15,8 @@ public:
|
|||||||
virtual void run();
|
virtual void run();
|
||||||
virtual void terminate();
|
virtual void terminate();
|
||||||
|
|
||||||
virtual void sink(AudioThreadInputPtr *input);
|
virtual void sink(AudioThreadInputPtr input) = 0;
|
||||||
virtual void inputChanged(AudioThreadInput oldProps, AudioThreadInputPtr newProps);
|
virtual void inputChanged(AudioThreadInput oldProps, AudioThreadInputPtr newProps) = 0;
|
||||||
|
|
||||||
virtual void setSinkName(std::string sinkName_in);
|
virtual void setSinkName(std::string sinkName_in);
|
||||||
virtual std::string getSinkName();
|
virtual std::string getSinkName();
|
||||||
@ -25,4 +25,5 @@ protected:
|
|||||||
std::recursive_mutex m_mutex;
|
std::recursive_mutex m_mutex;
|
||||||
AudioThreadInputQueuePtr inputQueuePtr;
|
AudioThreadInputQueuePtr inputQueuePtr;
|
||||||
std::string sinkName;
|
std::string sinkName;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -106,6 +106,7 @@ DemodulatorInstance::~DemodulatorInstance() {
|
|||||||
delete demodulatorPreThread;
|
delete demodulatorPreThread;
|
||||||
delete demodulatorThread;
|
delete demodulatorThread;
|
||||||
delete audioThread;
|
delete audioThread;
|
||||||
|
delete audioSinkThread;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -181,6 +182,10 @@ void DemodulatorInstance::terminate() {
|
|||||||
// std::cout << "Terminating demodulator preprocessor thread.." << std::endl;
|
// std::cout << "Terminating demodulator preprocessor thread.." << std::endl;
|
||||||
demodulatorPreThread->terminate();
|
demodulatorPreThread->terminate();
|
||||||
|
|
||||||
|
if (audioSinkThread != nullptr) {
|
||||||
|
audioSinkThread->terminate();
|
||||||
|
}
|
||||||
|
|
||||||
//that will actually unblock the currently blocked push().
|
//that will actually unblock the currently blocked push().
|
||||||
pipeIQInputData->flush();
|
pipeIQInputData->flush();
|
||||||
pipeAudioData->flush();
|
pipeAudioData->flush();
|
||||||
@ -204,6 +209,7 @@ bool DemodulatorInstance::isTerminated() {
|
|||||||
bool audioTerminated = audioThread->isTerminated();
|
bool audioTerminated = audioThread->isTerminated();
|
||||||
bool demodTerminated = demodulatorThread->isTerminated();
|
bool demodTerminated = demodulatorThread->isTerminated();
|
||||||
bool preDemodTerminated = demodulatorPreThread->isTerminated();
|
bool preDemodTerminated = demodulatorPreThread->isTerminated();
|
||||||
|
bool audioSinkTerminated = (audioSinkThread == nullptr) || audioSinkThread->isTerminated();
|
||||||
|
|
||||||
//Cleanup the worker threads, if the threads are indeed terminated.
|
//Cleanup the worker threads, if the threads are indeed terminated.
|
||||||
// threads are linked as t_PreDemod ==> t_Demod ==> t_Audio
|
// threads are linked as t_PreDemod ==> t_Demod ==> t_Audio
|
||||||
@ -240,14 +246,27 @@ bool DemodulatorInstance::isTerminated() {
|
|||||||
if (audioTerminated) {
|
if (audioTerminated) {
|
||||||
|
|
||||||
if (t_Audio) {
|
if (t_Audio) {
|
||||||
|
#ifdef __APPLE__
|
||||||
|
pthread_join(t_PreDemod, NULL);
|
||||||
|
#else
|
||||||
t_Audio->join();
|
t_Audio->join();
|
||||||
|
|
||||||
delete t_Audio;
|
delete t_Audio;
|
||||||
|
#endif
|
||||||
t_Audio = nullptr;
|
t_Audio = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool terminated = audioTerminated && demodTerminated && preDemodTerminated;
|
if (audioSinkTerminated) {
|
||||||
|
|
||||||
|
if (t_AudioSink != nullptr) {
|
||||||
|
t_AudioSink->join();
|
||||||
|
|
||||||
|
delete t_AudioSink;
|
||||||
|
t_AudioSink = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool terminated = audioTerminated && demodTerminated && preDemodTerminated && audioSinkTerminated;
|
||||||
|
|
||||||
return terminated;
|
return terminated;
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "ModemDigital.h"
|
#include "ModemDigital.h"
|
||||||
#include "ModemAnalog.h"
|
#include "ModemAnalog.h"
|
||||||
#include "AudioThread.h"
|
#include "AudioThread.h"
|
||||||
|
#include "AudioSinkThread.h"
|
||||||
|
|
||||||
#if ENABLE_DIGITAL_LAB
|
#if ENABLE_DIGITAL_LAB
|
||||||
#include "DigitalConsole.h"
|
#include "DigitalConsole.h"
|
||||||
@ -139,6 +140,10 @@ private:
|
|||||||
DemodulatorThread *demodulatorThread;
|
DemodulatorThread *demodulatorThread;
|
||||||
DemodulatorThreadControlCommandQueuePtr threadQueueControl;
|
DemodulatorThreadControlCommandQueuePtr threadQueueControl;
|
||||||
|
|
||||||
|
AudioSinkThread *audioSinkThread = nullptr;
|
||||||
|
std::thread *t_AudioSink = nullptr;
|
||||||
|
AudioThreadInputQueuePtr audioSinkInputQueue;
|
||||||
|
|
||||||
//protects child thread creation and termination
|
//protects child thread creation and termination
|
||||||
std::recursive_mutex m_thread_control_mutex;
|
std::recursive_mutex m_thread_control_mutex;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user