From 2f4eace8ab4cfd3b81c2c2632e297163930481b0 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Mon, 5 Aug 2024 22:28:33 +0200 Subject: [PATCH] initial commit --- .gitignore | 2 + CMakeLists.txt | 10 +++ dsp/block.cpp | 98 +++++++++++++++++++++++++++++ dsp/block.h | 80 +++++++++++++++++++++++ dsp/complex.h | 104 ++++++++++++++++++++++++++++++ dsp/mailbox.h | 72 +++++++++++++++++++++ dsp/processor.h | 12 ++++ dsp/stereo.h | 90 ++++++++++++++++++++++++++ dsp/stream.cpp | 143 ++++++++++++++++++++++++++++++++++++++++++ dsp/stream.h | 118 ++++++++++++++++++++++++++++++++++ dsp/taps.cpp | 97 ++++++++++++++++++++++++++++ dsp/taps.h | 55 ++++++++++++++++ dsp/taps/low_pass.cpp | 62 ++++++++++++++++++ dsp/taps/low_pass.h | 61 ++++++++++++++++++ src/main.cpp | 20 ++++++ 15 files changed, 1024 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 dsp/block.cpp create mode 100644 dsp/block.h create mode 100644 dsp/complex.h create mode 100644 dsp/mailbox.h create mode 100644 dsp/processor.h create mode 100644 dsp/stereo.h create mode 100644 dsp/stream.cpp create mode 100644 dsp/stream.h create mode 100644 dsp/taps.cpp create mode 100644 dsp/taps.h create mode 100644 dsp/taps/low_pass.cpp create mode 100644 dsp/taps/low_pass.h create mode 100644 src/main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..608b1d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode/ +build/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5def151 --- /dev/null +++ b/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/dsp/block.cpp b/dsp/block.cpp new file mode 100644 index 0000000..e9f360c --- /dev/null +++ b/dsp/block.cpp @@ -0,0 +1,98 @@ +#include "block.h" + +namespace dsp { + Block::Block() { + + } + + void Block::start() { + // Acquire worker variables + std::lock_guard 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 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 lck(workerMtx); + + // Return run state + return _running; + } + + void Block::registerInput(Signaler* input) { + // Acquire worker variables + std::lock_guard lck(workerMtx); + + // Save to the input list + inputs.push_back(input); + } + + void Block::unregisterInput(Signaler* input) { + // Acquire worker variables + std::lock_guard lck(workerMtx); + + // TODO + } + + void Block::registerOutput(Signaler* output) { + // Acquire worker variables + std::lock_guard lck(workerMtx); + + // Save to the output list + outputs.push_back(output); + } + + void Block::unregisterOutput(Signaler* output) { + // Acquire worker variables + std::lock_guard lck(workerMtx); + + // TODO + } + + void Block::worker() { + // Call the run function repeatedly + while (!run()); + } +} \ No newline at end of file diff --git a/dsp/block.h b/dsp/block.h new file mode 100644 index 0000000..b726946 --- /dev/null +++ b/dsp/block.h @@ -0,0 +1,80 @@ +#pragma once +#include +#include +#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 inputs; + std::vector outputs; + bool _running = false; + }; +} \ No newline at end of file diff --git a/dsp/complex.h b/dsp/complex.h new file mode 100644 index 0000000..f105bd5 --- /dev/null +++ b/dsp/complex.h @@ -0,0 +1,104 @@ +#pragma once +#include + +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 }; +} \ No newline at end of file diff --git a/dsp/mailbox.h b/dsp/mailbox.h new file mode 100644 index 0000000..f7098c2 --- /dev/null +++ b/dsp/mailbox.h @@ -0,0 +1,72 @@ +#pragma once +#include "stream.h" + +namespace gui { + /** + * Mailboxes allow to exchange objects between two threads. + */ + template + 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; + }; +} \ No newline at end of file diff --git a/dsp/processor.h b/dsp/processor.h new file mode 100644 index 0000000..86b3161 --- /dev/null +++ b/dsp/processor.h @@ -0,0 +1,12 @@ +#pragma once +#include "block.h" + +namespace dsp { + template + class Processor : public Block { + public: + + private: + + }; +} \ No newline at end of file diff --git a/dsp/stereo.h b/dsp/stereo.h new file mode 100644 index 0000000..33409fd --- /dev/null +++ b/dsp/stereo.h @@ -0,0 +1,90 @@ +#pragma once +#include + +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 }; + } +} \ No newline at end of file diff --git a/dsp/stream.cpp b/dsp/stream.cpp new file mode 100644 index 0000000..4d48e62 --- /dev/null +++ b/dsp/stream.cpp @@ -0,0 +1,143 @@ +#include "Stream.h" +#include "./complex.h" +#include "stereo.h" + +namespace dsp { + template + Stream::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 + Stream::~Stream() { + // Free both send and receive buffers + delete[] sendBuf; + delete[] recvBuf; + } + + template + void Stream::stopSender() { + // Acquire the sender variables + std::unique_lock slck(sendMtx); + + // Set the stop flag + stopSend = true; + + // Release the sender variables + slck.unlock(); + + // Notify the sender thread + sendCV.notify_all(); + } + + template + void Stream::clearSendStop() { + // Acquire the sender variables + std::unique_lock slck(sendMtx); + + // Clear the stop flag + stopSend = false; + } + + template + void Stream::stopReceiver() { + // Acquire the receiver variables + std::unique_lock rlck(recvMtx); + + // Set the stop flag + stopRecv = true; + + // Release the receiver variables + rlck.unlock(); + + // Notify the receiver thread + recvCV.notify_all(); + } + + template + void Stream::clearRecvStop() { + // Acquire the sender variables + std::unique_lock rlck(recvMtx); + + // Clear the stop flag + stopRecv = false; + } + + template + bool Stream::send(int count) { + // Acquire the sender variables + std::unique_lock 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 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 + int Stream::recv() { + // Acquire the receiver variables + std::unique_lock 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 + void Stream::ack() { + // Acquire the sender variables + std::unique_lock 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; + template class Stream; + template class Stream; + template class Stream; + template class Stream; + template class Stream; + template class Stream; + template class Stream; + template class Stream; + template class Stream; + template class Stream; + template class Stream; +} \ No newline at end of file diff --git a/dsp/stream.h b/dsp/stream.h new file mode 100644 index 0000000..cd68c76 --- /dev/null +++ b/dsp/stream.h @@ -0,0 +1,118 @@ +#pragma once +#include +#include + +#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 + 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; + }; +} \ No newline at end of file diff --git a/dsp/taps.cpp b/dsp/taps.cpp new file mode 100644 index 0000000..fbf615c --- /dev/null +++ b/dsp/taps.cpp @@ -0,0 +1,97 @@ +#include "taps.h" +#include "complex.h" +#include + +namespace dsp { + template + Taps::Taps() {} + + template + Taps::Taps(int count, bool zero) { + // Allocate buffer + reallocate(count); + + // Null out if requested + if (zero) { memset(data, 0, count*sizeof(T)); } + } + + template + Taps::Taps(T* taps, int count) { + // Allocate buffer + reallocate(count); + + // Copy data over + memcpy(data, taps, count*sizeof(T)); + } + + template + Taps::Taps(const Taps& b) { + // Allocate buffer + reallocate(b.count); + + // Copy data over + memcpy(data, b.data, b.count*sizeof(T)); + } + + template + Taps::Taps(Taps&& b) { + // Copy members + data = b.data; + count = b.count; + + // Neutralize old instance + b.data = NULL; + b.count = 0; + } + + template + Taps::~Taps() { + // Free the buffer if it is allocated + if (data) { delete[] data; } + } + + template + Taps& Taps::operator=(const Taps& b) { + // Reallocate buffer + reallocate(b.count); + + // Copy data over + memcpy(data, b.data, b.count*sizeof(T)); + + // Return self + return *this; + } + + template + Taps& Taps::operator=(Taps&& b) { + // Copy members + data = b.data; + count = b.count; + + // Neutralize old instance + b.data = NULL; + b.count = 0; + + // Return self + return *this; + } + + template + void Taps::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; + template class Taps; +} \ No newline at end of file diff --git a/dsp/taps.h b/dsp/taps.h new file mode 100644 index 0000000..df47e4d --- /dev/null +++ b/dsp/taps.h @@ -0,0 +1,55 @@ +#pragma once + +namespace dsp { + /** + * Filter tap container. + */ + template + 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& b); + Taps(Taps&& b); + ~Taps(); + + Taps& operator=(const Taps& b); + Taps& operator=(Taps&& 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; + }; +} \ No newline at end of file diff --git a/dsp/taps/low_pass.cpp b/dsp/taps/low_pass.cpp new file mode 100644 index 0000000..60bb311 --- /dev/null +++ b/dsp/taps/low_pass.cpp @@ -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 + } +} \ No newline at end of file diff --git a/dsp/taps/low_pass.h b/dsp/taps/low_pass.h new file mode 100644 index 0000000..1757f08 --- /dev/null +++ b/dsp/taps/low_pass.h @@ -0,0 +1,61 @@ +#pragma once +#include "../taps.h" + +namespace dsp::taps { + /** + * Low-pass filter taps. + */ + class LowPass : public Taps { + 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; + }; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..f113d4a --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,20 @@ +#include "dsp/taps.h" +#include "dsp/taps/low_pass.h" +#include "dsp/complex.h" +#include "dsp/stream.h" +#include + +struct TestStruct { + bool a; + int b; +}; + +int main() { + dsp::taps::LowPass lp(3000, 100, 15000); + + float test = lp[0]; + + dsp::Stream str; + + return 0; +} \ No newline at end of file