mirror of
https://github.com/AlexandreRouma/dsp2.git
synced 2026-04-20 07:22:44 +00:00
initial commit
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
.vscode/
|
||||||
|
build/
|
||||||
10
CMakeLists.txt
Normal file
10
CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
project(dsp)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE SRC "src/*.cpp" "dsp/*.cpp")
|
||||||
|
|
||||||
|
add_executable(${PROJECT_NAME} ${SRC})
|
||||||
|
|
||||||
|
target_include_directories(${PROJECT_NAME} PRIVATE "dsp/")
|
||||||
|
|
||||||
|
target_compile_options(${PROJECT_NAME} PRIVATE /std:c++20)
|
||||||
98
dsp/block.cpp
Normal file
98
dsp/block.cpp
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#include "block.h"
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
Block::Block() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::start() {
|
||||||
|
// Acquire worker variables
|
||||||
|
std::lock_guard<std::mutex> lck(workerMtx);
|
||||||
|
|
||||||
|
// Do nothing if the block is already running
|
||||||
|
if (_running) { return; }
|
||||||
|
|
||||||
|
// Mark as running
|
||||||
|
_running = true;
|
||||||
|
|
||||||
|
// Start the worker thread
|
||||||
|
workerThread = std::thread(&Block::worker, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::stop() {
|
||||||
|
// Acquire worker variables
|
||||||
|
std::lock_guard<std::mutex> lck(workerMtx);
|
||||||
|
|
||||||
|
// Do nothing if the block is not running
|
||||||
|
if (!_running) { return; }
|
||||||
|
|
||||||
|
// Set the receive stop flag on all input streams
|
||||||
|
for (const auto& input : inputs) {
|
||||||
|
input->stopReceiver();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the send stop flag on all output streams
|
||||||
|
for (const auto& output : outputs) {
|
||||||
|
output->stopSender();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the thread to exist
|
||||||
|
if (workerThread.joinable()) { workerThread.join(); }
|
||||||
|
|
||||||
|
// Clear the receive stop flag on all input streams
|
||||||
|
for (const auto& input : inputs) {
|
||||||
|
input->clearRecvStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the send stop flag on all output streams
|
||||||
|
for (const auto& output : outputs) {
|
||||||
|
output->clearSendStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark as not running
|
||||||
|
_running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Block::running() {
|
||||||
|
// Acquire worker variables
|
||||||
|
std::lock_guard<std::mutex> lck(workerMtx);
|
||||||
|
|
||||||
|
// Return run state
|
||||||
|
return _running;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::registerInput(Signaler* input) {
|
||||||
|
// Acquire worker variables
|
||||||
|
std::lock_guard<std::mutex> lck(workerMtx);
|
||||||
|
|
||||||
|
// Save to the input list
|
||||||
|
inputs.push_back(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::unregisterInput(Signaler* input) {
|
||||||
|
// Acquire worker variables
|
||||||
|
std::lock_guard<std::mutex> lck(workerMtx);
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::registerOutput(Signaler* output) {
|
||||||
|
// Acquire worker variables
|
||||||
|
std::lock_guard<std::mutex> lck(workerMtx);
|
||||||
|
|
||||||
|
// Save to the output list
|
||||||
|
outputs.push_back(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::unregisterOutput(Signaler* output) {
|
||||||
|
// Acquire worker variables
|
||||||
|
std::lock_guard<std::mutex> lck(workerMtx);
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void Block::worker() {
|
||||||
|
// Call the run function repeatedly
|
||||||
|
while (!run());
|
||||||
|
}
|
||||||
|
}
|
||||||
80
dsp/block.h
Normal file
80
dsp/block.h
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
#include "stream.h"
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
/**
|
||||||
|
* General DSP block class handling the worker thread start/stop operations.
|
||||||
|
* All input and output streams of the derived blocks must be registered using the appropriate functions.
|
||||||
|
*/
|
||||||
|
class Block {
|
||||||
|
public:
|
||||||
|
Block();
|
||||||
|
virtual ~Block();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the block's worker thread.
|
||||||
|
*/
|
||||||
|
void start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the block's worker thread.
|
||||||
|
*/
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check wether or not the block's worker thread is running.
|
||||||
|
*/
|
||||||
|
bool running();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Register an input stream.
|
||||||
|
* @param input Input stream to register.
|
||||||
|
*/
|
||||||
|
void registerInput(Signaler* input);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister an input stream.
|
||||||
|
* @param input Input stream to unregister.
|
||||||
|
*/
|
||||||
|
void unregisterInput(Signaler* input);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an output stream.
|
||||||
|
* @param input Output stream to register.
|
||||||
|
*/
|
||||||
|
void registerOutput(Signaler* output);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister an output stream.
|
||||||
|
* @param input Output stream to unregister.
|
||||||
|
*/
|
||||||
|
void unregisterOutput(Signaler* output);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the DSP code.
|
||||||
|
* @return True if the worker thread should stop.
|
||||||
|
*/
|
||||||
|
virtual bool run();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mutex to be used for block settings.
|
||||||
|
*/
|
||||||
|
std::recursive_mutex settingsMtx;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Worker thread function.
|
||||||
|
*/
|
||||||
|
void worker();
|
||||||
|
|
||||||
|
// Worker variables
|
||||||
|
std::mutex workerMtx;
|
||||||
|
std::thread workerThread;
|
||||||
|
std::vector<Signaler*> inputs;
|
||||||
|
std::vector<Signaler*> outputs;
|
||||||
|
bool _running = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
104
dsp/complex.h
Normal file
104
dsp/complex.h
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
/**
|
||||||
|
* Complex 32bit floating-point number.
|
||||||
|
*/
|
||||||
|
struct Complex {
|
||||||
|
// TODO: Allow construction from a float
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the conjugate of the Complex number.
|
||||||
|
*/
|
||||||
|
constexpr inline Complex conj() const {
|
||||||
|
return Complex{ re, -im };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the phase of the Complex number normalized between -pi and pi.
|
||||||
|
*/
|
||||||
|
inline float phase() const {
|
||||||
|
return atan2f(im, re);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the amplitude of the Complex number.
|
||||||
|
*/
|
||||||
|
inline float amplitude() const {
|
||||||
|
return sqrtf(re*re + im*im);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator=(float b) {
|
||||||
|
re = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Real component.
|
||||||
|
*/
|
||||||
|
float re;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imaginary component.
|
||||||
|
*/
|
||||||
|
float im;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Complex operator+(const dsp::Complex& a, float b) {
|
||||||
|
return dsp::Complex{ a.re + b, a.im };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Complex operator+(float a, const dsp::Complex& b) {
|
||||||
|
return dsp::Complex{ a + b.re, b.im };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Complex operator-(const dsp::Complex& a, float b) {
|
||||||
|
return dsp::Complex{ a.re - b, a.im };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Complex operator-(float a, const dsp::Complex& b) {
|
||||||
|
return dsp::Complex{ a - b.re, b.im };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Complex operator*(const dsp::Complex& a, float b) {
|
||||||
|
return dsp::Complex{ a.re*b, a.im*b };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Complex operator*(float a, const dsp::Complex& b) {
|
||||||
|
return dsp::Complex{ a*b.re, a*b.im };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Complex operator/(const dsp::Complex& a, float b) {
|
||||||
|
return dsp::Complex{ a.re/b, a.im/b };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Complex operator/(float a, const dsp::Complex& b) {
|
||||||
|
float denom = b.re*b.re + b.im*b.im;
|
||||||
|
return dsp::Complex{ a*b.re / denom, -a*b.im / denom };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Complex operator+(const dsp::Complex& a, const dsp::Complex& b) {
|
||||||
|
return dsp::Complex{ a.re + b.re, a.im + b.im };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Complex operator-(const dsp::Complex& a, const dsp::Complex& b) {
|
||||||
|
return dsp::Complex{ a.re - b.re, a.im - b.im };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Complex operator*(const dsp::Complex& a, const dsp::Complex& b) {
|
||||||
|
return dsp::Complex{ a.re*b.re - a.im*b.im, a.im*b.re + a.re*b.im };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Complex operator/(const dsp::Complex& a, const dsp::Complex& b) {
|
||||||
|
float denom = b.re*b.re + b.im*b.im;
|
||||||
|
return dsp::Complex{ (a.re*b.re + a.im*b.im) / denom, (a.im*b.re - a.re*b.im) / denom };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Complex operator""_j(unsigned long long value) {
|
||||||
|
return dsp::Complex{ 0.0f, (float)value };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Complex operator""_j(long double value) {
|
||||||
|
return dsp::Complex{ 0.0f, (float)value };
|
||||||
|
}
|
||||||
72
dsp/mailbox.h
Normal file
72
dsp/mailbox.h
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "stream.h"
|
||||||
|
|
||||||
|
namespace gui {
|
||||||
|
/**
|
||||||
|
* Mailboxes allow to exchange objects between two threads.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
class Mailbox : public Signaler {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a mailbox object.
|
||||||
|
*/
|
||||||
|
Mailbox();
|
||||||
|
|
||||||
|
~Mailbox();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the sending thread that it should stop.
|
||||||
|
*/
|
||||||
|
void stopSender();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the receiving thread that it should stop.
|
||||||
|
*/
|
||||||
|
void stopReceiver();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an object.
|
||||||
|
* @return True if the sender thread must exist, false otherwise.
|
||||||
|
*/
|
||||||
|
bool send();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for an object. May also return in case of a signal to exit. ack() must be called as soon as the received object has been processed.
|
||||||
|
* @return True if the receiver thread must exist, false otherwise.
|
||||||
|
*/
|
||||||
|
bool recv();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acknowledge reception and processing of the samples. Allows sender thread to send a new buffer.
|
||||||
|
*/
|
||||||
|
void ack();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the sending object.
|
||||||
|
* @return Sending object.
|
||||||
|
*/
|
||||||
|
T* getSendObject() { return sendObj; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the receiving object.
|
||||||
|
* @return Sending object.
|
||||||
|
*/
|
||||||
|
const T* getRecvObject() { return recvObj; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Sender variables
|
||||||
|
std::condition_variable sendCV;
|
||||||
|
std::mutex sendMtx;
|
||||||
|
bool canSend = true;
|
||||||
|
bool stopSend = false;
|
||||||
|
T* sendObj;
|
||||||
|
|
||||||
|
// Receiver variables
|
||||||
|
std::condition_variable recvCV;
|
||||||
|
std::mutex recvMtx;
|
||||||
|
bool available = false;
|
||||||
|
bool stopRecv = false;
|
||||||
|
T* recvObj;
|
||||||
|
};
|
||||||
|
}
|
||||||
12
dsp/processor.h
Normal file
12
dsp/processor.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "block.h"
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
template <typename I, typename O>
|
||||||
|
class Processor : public Block {
|
||||||
|
public:
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
90
dsp/stereo.h
Normal file
90
dsp/stereo.h
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
/**
|
||||||
|
* Two 32bit floating-point number representing the left and right channels.
|
||||||
|
*/
|
||||||
|
struct Stereo {
|
||||||
|
// TODO: Allow construction from a float
|
||||||
|
|
||||||
|
void operator=(float b) {
|
||||||
|
l = b;
|
||||||
|
r = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Left channel.
|
||||||
|
*/
|
||||||
|
float l;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Right channel.
|
||||||
|
*/
|
||||||
|
float r;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline constexpr dsp::Stereo operator+(const dsp::Stereo& a, float b) {
|
||||||
|
return dsp::Stereo{ a.l + b, a.r + b};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Stereo operator+(float a, const dsp::Stereo& b) {
|
||||||
|
return dsp::Stereo{ a + b.l, a + b.r };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Stereo operator-(const dsp::Stereo& a, float b) {
|
||||||
|
return dsp::Stereo{ a.l - b, a.r - b };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Stereo operator-(float a, const dsp::Stereo& b) {
|
||||||
|
return dsp::Stereo{ a - b.l, a - b.r };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Stereo operator*(const dsp::Stereo& a, float b) {
|
||||||
|
return dsp::Stereo{ a.l*b, a.r*b };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Stereo operator*(float a, const dsp::Stereo& b) {
|
||||||
|
return dsp::Stereo{ a*b.l, a*b.r };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Stereo operator/(const dsp::Stereo& a, float b) {
|
||||||
|
return dsp::Stereo{ a.l/b, a.r/b };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Stereo operator/(float a, const dsp::Stereo& b) {
|
||||||
|
return dsp::Stereo{ a/b.l, a/b.r };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Stereo operator+(const dsp::Stereo& a, const dsp::Stereo& b) {
|
||||||
|
return dsp::Stereo{ a.l + b.l, a.r + b.r };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Stereo operator-(const dsp::Stereo& a, const dsp::Stereo& b) {
|
||||||
|
return dsp::Stereo{ a.l - b.l, a.r - b.r };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Stereo operator*(const dsp::Stereo& a, const dsp::Stereo& b) {
|
||||||
|
return dsp::Stereo{ a.l*b.l, a.r*b.r };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Stereo operator/(const dsp::Stereo& a, const dsp::Stereo& b) {
|
||||||
|
return dsp::Stereo{ a.l/b.l, a.r/b.r };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Stereo operator""_L(unsigned long long value) {
|
||||||
|
return dsp::Stereo{ (float)value, 0.0f };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Stereo operator""_L(long double value) {
|
||||||
|
return dsp::Stereo{ (float)value, 0.0f };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Stereo operator""_R(unsigned long long value) {
|
||||||
|
return dsp::Stereo{ 0.0f, (float)value };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr dsp::Stereo operator""_R(long double value) {
|
||||||
|
return dsp::Stereo{ 0.0f, (float)value };
|
||||||
|
}
|
||||||
|
}
|
||||||
143
dsp/stream.cpp
Normal file
143
dsp/stream.cpp
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
#include "Stream.h"
|
||||||
|
#include "./complex.h"
|
||||||
|
#include "stereo.h"
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
template <typename T>
|
||||||
|
Stream<T>::Stream(int channels, int bufferSize) {
|
||||||
|
// Allocate both send and receive buffers aligned by the size of the type
|
||||||
|
sendBuf = new T[bufferSize, sizeof(T)];
|
||||||
|
recvBuf = new T[bufferSize, sizeof(T)];
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Stream<T>::~Stream() {
|
||||||
|
// Free both send and receive buffers
|
||||||
|
delete[] sendBuf;
|
||||||
|
delete[] recvBuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Stream<T>::stopSender() {
|
||||||
|
// Acquire the sender variables
|
||||||
|
std::unique_lock<std::mutex> slck(sendMtx);
|
||||||
|
|
||||||
|
// Set the stop flag
|
||||||
|
stopSend = true;
|
||||||
|
|
||||||
|
// Release the sender variables
|
||||||
|
slck.unlock();
|
||||||
|
|
||||||
|
// Notify the sender thread
|
||||||
|
sendCV.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Stream<T>::clearSendStop() {
|
||||||
|
// Acquire the sender variables
|
||||||
|
std::unique_lock<std::mutex> slck(sendMtx);
|
||||||
|
|
||||||
|
// Clear the stop flag
|
||||||
|
stopSend = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Stream<T>::stopReceiver() {
|
||||||
|
// Acquire the receiver variables
|
||||||
|
std::unique_lock<std::mutex> rlck(recvMtx);
|
||||||
|
|
||||||
|
// Set the stop flag
|
||||||
|
stopRecv = true;
|
||||||
|
|
||||||
|
// Release the receiver variables
|
||||||
|
rlck.unlock();
|
||||||
|
|
||||||
|
// Notify the receiver thread
|
||||||
|
recvCV.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Stream<T>::clearRecvStop() {
|
||||||
|
// Acquire the sender variables
|
||||||
|
std::unique_lock<std::mutex> rlck(recvMtx);
|
||||||
|
|
||||||
|
// Clear the stop flag
|
||||||
|
stopRecv = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Stream<T>::send(int count) {
|
||||||
|
// Acquire the sender variables
|
||||||
|
std::unique_lock<std::mutex> slck(sendMtx);
|
||||||
|
|
||||||
|
// Wait until the sender can send or is notified it should stop
|
||||||
|
sendCV.wait(slck, [=](){ return canSend || stopSend; });
|
||||||
|
|
||||||
|
// If asked to stop, return true
|
||||||
|
if (stopSend) { return true; }
|
||||||
|
|
||||||
|
// Mark that data can no longer be sent
|
||||||
|
canSend = false;
|
||||||
|
|
||||||
|
// Acquire the receiver variables
|
||||||
|
std::unique_lock<std::mutex> rlck(recvMtx);
|
||||||
|
|
||||||
|
// Swap buffers
|
||||||
|
T* tmp = sendBuf;
|
||||||
|
sendBuf = recvBuf;
|
||||||
|
recvBuf = tmp;
|
||||||
|
|
||||||
|
// Release the sender variables
|
||||||
|
slck.unlock();
|
||||||
|
|
||||||
|
// Set the number of items that are readable
|
||||||
|
available = count;
|
||||||
|
|
||||||
|
// Release the receiver variables
|
||||||
|
rlck.unlock();
|
||||||
|
|
||||||
|
// Notify the receiver thread that there are items available
|
||||||
|
recvCV.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
int Stream<T>::recv() {
|
||||||
|
// Acquire the receiver variables
|
||||||
|
std::unique_lock<std::mutex> rlck(recvMtx);
|
||||||
|
|
||||||
|
// Wait until there are items that are readable or the receiver is notified that it should stop
|
||||||
|
recvCV.wait(rlck, [=](){ return available || stopRecv; });
|
||||||
|
|
||||||
|
// Return the number of readable items or -1 if the receiver should stop
|
||||||
|
return stopRecv ? -1 : available;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Stream<T>::ack() {
|
||||||
|
// Acquire the sender variables
|
||||||
|
std::unique_lock<std::mutex> slck(sendMtx);
|
||||||
|
|
||||||
|
// Mark that data can be sent
|
||||||
|
canSend = true;
|
||||||
|
|
||||||
|
// Release the sender variables
|
||||||
|
slck.unlock();
|
||||||
|
|
||||||
|
// Notify the sender thread
|
||||||
|
sendCV.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instantiate the class
|
||||||
|
template class Stream<uint8_t>;
|
||||||
|
template class Stream<uint16_t>;
|
||||||
|
template class Stream<uint32_t>;
|
||||||
|
template class Stream<uint64_t>;
|
||||||
|
template class Stream<int8_t>;
|
||||||
|
template class Stream<int16_t>;
|
||||||
|
template class Stream<int32_t>;
|
||||||
|
template class Stream<int64_t>;
|
||||||
|
template class Stream<float>;
|
||||||
|
template class Stream<double>;
|
||||||
|
template class Stream<Complex>;
|
||||||
|
template class Stream<Stereo>;
|
||||||
|
}
|
||||||
118
dsp/stream.h
Normal file
118
dsp/stream.h
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
|
#define DSP_DEFAULT_BUFFER_SIZE 1000000
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
/**
|
||||||
|
* Represents a class that can signal to its acting threads to stop. (TODO: Better name)
|
||||||
|
*/
|
||||||
|
class Signaler {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Notify the sending thread that it should stop.
|
||||||
|
*/
|
||||||
|
virtual void stopSender() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the sender stop flag to allow restarting the sender thread.
|
||||||
|
*/
|
||||||
|
virtual void clearSendStop() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the receiving thread that it should stop.
|
||||||
|
*/
|
||||||
|
virtual void stopReceiver() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the receiver stop flag to allow restarting the sender thread.
|
||||||
|
*/
|
||||||
|
virtual void clearRecvStop() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Streams allow to exchange samples between two threads.
|
||||||
|
* The samples have to be of type (u)int8_t, (u)int16_t, (u)int32_t, (u)int64_t, float, double or Complex.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
class Stream : public Signaler {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a stream object.
|
||||||
|
* @param bufferSize Number of items in the buffers.
|
||||||
|
*/
|
||||||
|
Stream(int channels = 1, int bufferSize = DSP_DEFAULT_BUFFER_SIZE);
|
||||||
|
|
||||||
|
~Stream();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the sending thread that it should stop. clearSendStop() must be called once the thread is stopped to clear the stop flag.
|
||||||
|
*/
|
||||||
|
void stopSender();
|
||||||
|
|
||||||
|
// TODO: More consistent naming
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the sender stop flag to allow restarting the sender thread.
|
||||||
|
*/
|
||||||
|
void clearSendStop();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the receiving thread that it should stop. clearRecvStop() must be called once the thread is stopped to clear the stop flag.
|
||||||
|
*/
|
||||||
|
void stopReceiver();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the receiver stop flag to allow restarting the sender thread.
|
||||||
|
*/
|
||||||
|
void clearRecvStop();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a buffer of samples.
|
||||||
|
* @param count Number of samples in the send buffer.
|
||||||
|
* @return True if the sender thread must exist, false otherwise.
|
||||||
|
*/
|
||||||
|
bool send(int count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for buffer of samples. May also return in case of a signal to exit. ack() must be called as soon as the receive buffer has been entirely processed.
|
||||||
|
* @return Number of samples or -1 if the worker thread must exit.
|
||||||
|
*/
|
||||||
|
int recv();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acknowledge reception and processing of the samples. Allows sender thread to send a new buffer.
|
||||||
|
*/
|
||||||
|
void ack();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the sending buffer.
|
||||||
|
* @param channel ID of the channel to get the buffer for.
|
||||||
|
* @return Sending buffer.
|
||||||
|
*/
|
||||||
|
T* getSendBuffer(int channel = 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the receiving buffer.
|
||||||
|
* @param channel ID of the channel to get the buffer for.
|
||||||
|
* @return Sending buffer.
|
||||||
|
*/
|
||||||
|
const T* getRecvBuffer(int channel = 0);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Sender variables
|
||||||
|
std::condition_variable sendCV;
|
||||||
|
std::mutex sendMtx;
|
||||||
|
bool canSend = true;
|
||||||
|
bool stopSend = false;
|
||||||
|
T* sendBuf;
|
||||||
|
|
||||||
|
// Receiver variables
|
||||||
|
std::condition_variable recvCV;
|
||||||
|
std::mutex recvMtx;
|
||||||
|
int available = 0;
|
||||||
|
bool stopRecv = false;
|
||||||
|
T* recvBuf;
|
||||||
|
};
|
||||||
|
}
|
||||||
97
dsp/taps.cpp
Normal file
97
dsp/taps.cpp
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
#include "taps.h"
|
||||||
|
#include "complex.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
template <class T>
|
||||||
|
Taps<T>::Taps() {}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
Taps<T>::Taps(int count, bool zero) {
|
||||||
|
// Allocate buffer
|
||||||
|
reallocate(count);
|
||||||
|
|
||||||
|
// Null out if requested
|
||||||
|
if (zero) { memset(data, 0, count*sizeof(T)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
Taps<T>::Taps(T* taps, int count) {
|
||||||
|
// Allocate buffer
|
||||||
|
reallocate(count);
|
||||||
|
|
||||||
|
// Copy data over
|
||||||
|
memcpy(data, taps, count*sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
Taps<T>::Taps(const Taps<T>& b) {
|
||||||
|
// Allocate buffer
|
||||||
|
reallocate(b.count);
|
||||||
|
|
||||||
|
// Copy data over
|
||||||
|
memcpy(data, b.data, b.count*sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
Taps<T>::Taps(Taps<T>&& b) {
|
||||||
|
// Copy members
|
||||||
|
data = b.data;
|
||||||
|
count = b.count;
|
||||||
|
|
||||||
|
// Neutralize old instance
|
||||||
|
b.data = NULL;
|
||||||
|
b.count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
Taps<T>::~Taps() {
|
||||||
|
// Free the buffer if it is allocated
|
||||||
|
if (data) { delete[] data; }
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
Taps<T>& Taps<T>::operator=(const Taps<T>& b) {
|
||||||
|
// Reallocate buffer
|
||||||
|
reallocate(b.count);
|
||||||
|
|
||||||
|
// Copy data over
|
||||||
|
memcpy(data, b.data, b.count*sizeof(T));
|
||||||
|
|
||||||
|
// Return self
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
Taps<T>& Taps<T>::operator=(Taps<T>&& b) {
|
||||||
|
// Copy members
|
||||||
|
data = b.data;
|
||||||
|
count = b.count;
|
||||||
|
|
||||||
|
// Neutralize old instance
|
||||||
|
b.data = NULL;
|
||||||
|
b.count = 0;
|
||||||
|
|
||||||
|
// Return self
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void Taps<T>::reallocate(int count) {
|
||||||
|
// If the new count is no different and the buffer is allocated, no need to realloc
|
||||||
|
if (data && this->count == count) { return; }
|
||||||
|
|
||||||
|
// Free buffer
|
||||||
|
if (data) { delete[] data; }
|
||||||
|
|
||||||
|
// Allocate buffer
|
||||||
|
data = new T[count, sizeof(T)];
|
||||||
|
|
||||||
|
// Update tap count
|
||||||
|
this->count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instantiate the class
|
||||||
|
template class Taps<float>;
|
||||||
|
template class Taps<Complex>;
|
||||||
|
}
|
||||||
55
dsp/taps.h
Normal file
55
dsp/taps.h
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
/**
|
||||||
|
* Filter tap container.
|
||||||
|
*/
|
||||||
|
template <class T>
|
||||||
|
class Taps {
|
||||||
|
public:
|
||||||
|
Taps();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a tap bank holding count taps.
|
||||||
|
* @param count Number of taps.
|
||||||
|
* @param zero Zero out the taps.
|
||||||
|
*/
|
||||||
|
Taps(int count, bool zero = true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a tap bank from an array.
|
||||||
|
* @param taps Array contianing taps.
|
||||||
|
* @param count Number of taps to load.
|
||||||
|
*/
|
||||||
|
Taps(T* taps, int count);
|
||||||
|
|
||||||
|
Taps(const Taps<T>& b);
|
||||||
|
Taps(Taps<T>&& b);
|
||||||
|
~Taps();
|
||||||
|
|
||||||
|
Taps<T>& operator=(const Taps<T>& b);
|
||||||
|
Taps<T>& operator=(Taps<T>&& b);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of taps in the filter.
|
||||||
|
*/
|
||||||
|
inline int size() const { return count; }
|
||||||
|
|
||||||
|
inline operator const T*() const { return data; }
|
||||||
|
|
||||||
|
inline operator bool() const { return data && count; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a tap by index.
|
||||||
|
* @param index Index of the tap
|
||||||
|
* @return Tap at index.
|
||||||
|
*/
|
||||||
|
inline const T& operator[](int index) const { return data[index]; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void reallocate(int count);
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
T* data = nullptr;
|
||||||
|
};
|
||||||
|
}
|
||||||
62
dsp/taps/low_pass.cpp
Normal file
62
dsp/taps/low_pass.cpp
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#include "low_pass.h"
|
||||||
|
|
||||||
|
namespace dsp::taps {
|
||||||
|
LowPass::LowPass() {}
|
||||||
|
|
||||||
|
LowPass::LowPass(float cutoff, float transWidth, float samplerate) {
|
||||||
|
// Save parameters
|
||||||
|
this->cutoff = cutoff;
|
||||||
|
this->transWidth = transWidth;
|
||||||
|
this->samplerate = samplerate;
|
||||||
|
|
||||||
|
// Generate filter
|
||||||
|
generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
float LowPass::getCutoff() {
|
||||||
|
return cutoff;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LowPass::setCutoff(float cutoff, float transWidth) {
|
||||||
|
// Update parameter
|
||||||
|
this->cutoff = cutoff;
|
||||||
|
|
||||||
|
// If the transition width is given, update is as well
|
||||||
|
if (transWidth > 0) { this->transWidth = transWidth; }
|
||||||
|
|
||||||
|
// Regenerate filter
|
||||||
|
generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
float LowPass::getTransWidth() {
|
||||||
|
return transWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LowPass::setTransWidth(float transWidth) {
|
||||||
|
// Update parameter
|
||||||
|
this->transWidth = transWidth;
|
||||||
|
|
||||||
|
// Regenerate filter
|
||||||
|
generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
float LowPass::getSamplerate() {
|
||||||
|
return samplerate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LowPass::setSamplerate(float samplerate) {
|
||||||
|
// Update parameter
|
||||||
|
this->samplerate = samplerate;
|
||||||
|
|
||||||
|
// Regenerate filter
|
||||||
|
generate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LowPass::generate() {
|
||||||
|
// Reallocate the buffer
|
||||||
|
reallocate(0 /*TODO: Tap count estimation*/);
|
||||||
|
|
||||||
|
// Generate taps
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
61
dsp/taps/low_pass.h
Normal file
61
dsp/taps/low_pass.h
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "../taps.h"
|
||||||
|
|
||||||
|
namespace dsp::taps {
|
||||||
|
/**
|
||||||
|
* Low-pass filter taps.
|
||||||
|
*/
|
||||||
|
class LowPass : public Taps<float> {
|
||||||
|
public:
|
||||||
|
LowPass();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create low-pass FIR taps.
|
||||||
|
* @param cutoff 3dB cutoff frequency in Hz.
|
||||||
|
* @param transWidth Transition width in Hz.
|
||||||
|
* @param samplerate Sample rate in Hz.
|
||||||
|
*/
|
||||||
|
LowPass(float cutoff, float transWidth, float samplerate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cutoff frequency.
|
||||||
|
*/
|
||||||
|
float getCutoff();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set cutoff frequency.
|
||||||
|
* @param cutoff Cutoff frequency in Hz.
|
||||||
|
* @param transWidth Transition width in Hz. Negative if unchanged.
|
||||||
|
*/
|
||||||
|
void setCutoff(float cutoff, float transWidth = -1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get transition width.
|
||||||
|
*/
|
||||||
|
float getTransWidth();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set transition width.
|
||||||
|
* @param transWidth Transition width in Hz.
|
||||||
|
*/
|
||||||
|
void setTransWidth(float transWidth);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get sample rate.
|
||||||
|
*/
|
||||||
|
float getSamplerate();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set sample rate.
|
||||||
|
* @param samplerate Sample rate in Hz.
|
||||||
|
*/
|
||||||
|
void setSamplerate(float samplerate);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void generate();
|
||||||
|
|
||||||
|
float cutoff;
|
||||||
|
float transWidth;
|
||||||
|
float samplerate;
|
||||||
|
};
|
||||||
|
}
|
||||||
20
src/main.cpp
Normal file
20
src/main.cpp
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#include "dsp/taps.h"
|
||||||
|
#include "dsp/taps/low_pass.h"
|
||||||
|
#include "dsp/complex.h"
|
||||||
|
#include "dsp/stream.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
struct TestStruct {
|
||||||
|
bool a;
|
||||||
|
int b;
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
dsp::taps::LowPass lp(3000, 100, 15000);
|
||||||
|
|
||||||
|
float test = lp[0];
|
||||||
|
|
||||||
|
dsp::Stream<float> str;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user