mirror of
https://github.com/AlexandreRouma/dsp2.git
synced 2026-04-18 14:52:43 +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