--- /dev/null
+/**
+ * 1_convolve_simple.cpp
+ * Written by Clyne Sullivan.
+ *
+ * Computes a convolution in the simplest way possible. While the code is brief, it lacks many
+ * possible optimizations. The convolution's result will not fill the output buffer either, as the
+ * transient response is not calculated.
+ */
+
+Sample *process_data(Samples samples)
+{
+ // Define our output buffer. SIZE is the largest size of the 'samples' buffer.
+ static Sample buffer[samples.size()];
+
+ // Define our filter
+ constexpr unsigned int filter_size = 3;
+ float filter[filter_size] = {
+ 0.3333, 0.3333, 0.3333
+ };
+
+ // Begin convolving:
+ for (int n = 0; n < samples.size() - (filter_size - 1); n++) {
+ buffer[n] = 0;
+ for (int k = 0; k < filter_size; k++)
+ buffer[n] += samples[n + k] * filter[k];
+ }
+
+ return buffer;
+}
--- /dev/null
+/**
+ * 2_convolve_overlap_save.cpp
+ * Written by Clyne Sullivan.
+ *
+ * This convolution examples takes an overlap-save approach, where samples from the previous run
+ * are saved so that the overall operation is not interrupted (i.e. the observed output will
+ * transition smoothly between processed "chunks").
+ *
+ * Note that there are still improvements that can be made to the code; for example, notice every
+ * spot where an integer/float conversion is necessary. Operations like these may slow down the
+ * computation.
+ */
+
+Sample *process_data(Samples samples)
+{
+ static Sample buffer[samples.size()];
+
+ constexpr unsigned int filter_size = 3;
+ float filter[filter_size] = {
+ 0.3333, 0.3333, 0.3333
+ };
+
+ // Keep a buffer of extra samples for overlap-save
+ static Sample prev[filter_size];
+
+ for (int n = 0; n < samples.size(); n++) {
+ buffer[n] = 0;
+
+ for (int k = 0; k < filter_size; k++) {
+ int i = n - (filter_size - 1) + k;
+
+ // If i is >= 0, access current sample buffer.
+ // If i is < 0, provide the previous samples from the 'prev' buffer
+ if (i >= 0)
+ buffer[n] += samples[i] * filter[k];
+ else
+ buffer[n] += prev[filter_size - 1 + i] * filter[k];
+ }
+ }
+
+ // Save samples for the next convolution run
+ for (int i = 0; i < filter_size; i++)
+ prev[i] = samples[samples.size() - filter_size + i];
+
+ return buffer;
+}
+
--- /dev/null
+/**
+ * 3_fir.cpp
+ * Written by Clyne Sullivan.
+ *
+ * The below code was written for applying FIR filters. While this is still essentially an overlap-
+ * save convolution, other optimizations have been made to allow for larger filters to be applied
+ * within the available execution time. Samples are also normalized so that they center around zero.
+ */
+
+Sample *process_data(Samples samples)
+{
+ static Sample buffer[samples.size()];
+
+ // Define the filter:
+ constexpr unsigned int filter_size = 3;
+ static float filter[filter_size] = {
+ // Put filter values here (note: precision will be truncated for 'float' size).
+ 0.3333, 0.3333, 0.3333
+ };
+
+ // Do an overlap-save convolution
+ static Sample prev[filter_size];
+
+ for (int n = 0; n < samples.size(); n++) {
+ // Using a float variable for accumulation allows for better code optimization
+ float v = 0;
+
+ for (int k = 0; k < filter_size; k++) {
+ int i = n - (filter_size - 1) + k;
+
+ auto s = i >= 0 ? samples[i] : prev[filter_size - 1 + i];
+ // Sample values are 0 to 4095. Below, the original sample is normalized to a -1.0 to
+ // 1.0 range for calculation.
+ v += (s / 2048.f - 1) * filter[k];
+ }
+
+ // Return value to sample range of 0-4095.
+ buffer[n] = (v + 1) * 2048.f;
+ }
+
+ // Save samples for next convolution
+ for (int i = 0; i < filter_size; i++)
+ prev[i] = samples[samples.size() - filter_size + i];
+
+ return buffer;
+}
+
--- /dev/null
+#include <cstdint>
+using float32_t = float;
+
+typedef struct
+{
+ uint16_t numTaps; /**< number of filter coefficients in the filter. */
+ float32_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */
+ float32_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps. */
+} arm_fir_instance_f32;
+
+static void arm_fir_f32(const arm_fir_instance_f32 * S, float32_t * pSrc, float32_t * pDst, uint32_t blockSize);
+
+Sample *process_data(Samples samples)
+{
+ // 1. Define our array sizes (Be sure to set Run > Set buffer size... to below value!)
+ constexpr unsigned int buffer_size = 500;
+ constexpr unsigned int filter_size = 100;
+
+ // 2. Define our filter and the working arrays
+ static float filter[filter_size] = {
+ .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
+ .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
+ .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
+ .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
+ .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
+ .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
+ .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
+ .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
+ .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
+ .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f
+ };
+ static float input[buffer_size];
+ static float output[buffer_size];
+ static float working[buffer_size + filter_size];
+
+ // 3. Scale 0-4095 interger sample values to +/- 1.0 floats
+ for (unsigned int i = 0; i < samples.size(); i++)
+ input[i] = (samples[i] - 2048) / 2048.f;
+
+ // 4. Compute the FIR
+ arm_fir_instance_f32 fir { filter_size, working, filter };
+ arm_fir_f32(&fir, input, output, samples.size());
+
+ // 5. Convert float results back to 0-4095 range for output
+ for (unsigned int i = 0; i < samples.size(); i++)
+ samples[i] = output[i] * 2048.f + 2048;
+
+ return samples.data();
+}
+
+// Below taken from the CMSIS DSP Library (find it on GitHub)
+void arm_fir_f32(
+ const arm_fir_instance_f32 * S,
+ float32_t * pSrc,
+ float32_t * pDst,
+ uint32_t blockSize)
+{
+ float32_t *pState = S->pState; /* State pointer */
+ float32_t *pCoeffs = S->pCoeffs; /* Coefficient pointer */
+ float32_t *pStateCurnt; /* Points to the current sample of the state */
+ float32_t *px, *pb; /* Temporary pointers for state and coefficient buffers */
+ float32_t acc0, acc1, acc2, acc3, acc4, acc5, acc6, acc7; /* Accumulators */
+ float32_t x0, x1, x2, x3, x4, x5, x6, x7, c0; /* Temporary variables to hold state and coefficient values */
+ uint32_t numTaps = S->numTaps; /* Number of filter coefficients in the filter */
+ uint32_t i, tapCnt, blkCnt; /* Loop counters */
+ float32_t p0,p1,p2,p3,p4,p5,p6,p7; /* Temporary product values */
+
+ /* S->pState points to state array which contains previous frame (numTaps - 1) samples */
+ /* pStateCurnt points to the location where the new input data should be written */
+ pStateCurnt = &(S->pState[(numTaps - 1u)]);
+
+ /* Apply loop unrolling and compute 8 output values simultaneously.
+ * The variables acc0 ... acc7 hold output values that are being computed:
+ *
+ * acc0 = b[numTaps-1] * x[n-numTaps-1] + b[numTaps-2] * x[n-numTaps-2] + b[numTaps-3] * x[n-numTaps-3] +...+ b[0] * x[0]
+ * acc1 = b[numTaps-1] * x[n-numTaps] + b[numTaps-2] * x[n-numTaps-1] + b[numTaps-3] * x[n-numTaps-2] +...+ b[0] * x[1]
+ * acc2 = b[numTaps-1] * x[n-numTaps+1] + b[numTaps-2] * x[n-numTaps] + b[numTaps-3] * x[n-numTaps-1] +...+ b[0] * x[2]
+ * acc3 = b[numTaps-1] * x[n-numTaps+2] + b[numTaps-2] * x[n-numTaps+1] + b[numTaps-3] * x[n-numTaps] +...+ b[0] * x[3]
+ */
+ blkCnt = blockSize >> 3;
+
+ /* First part of the processing with loop unrolling. Compute 8 outputs at a time.
+ ** a second loop below computes the remaining 1 to 7 samples. */
+ while(blkCnt > 0u)
+ {
+ /* Copy four new input samples into the state buffer */
+ *pStateCurnt++ = *pSrc++;
+ *pStateCurnt++ = *pSrc++;
+ *pStateCurnt++ = *pSrc++;
+ *pStateCurnt++ = *pSrc++;
+
+ /* Set all accumulators to zero */
+ acc0 = 0.0f;
+ acc1 = 0.0f;
+ acc2 = 0.0f;
+ acc3 = 0.0f;
+ acc4 = 0.0f;
+ acc5 = 0.0f;
+ acc6 = 0.0f;
+ acc7 = 0.0f;
+
+ /* Initialize state pointer */
+ px = pState;
+
+ /* Initialize coeff pointer */
+ pb = (pCoeffs);
+
+ /* This is separated from the others to avoid
+ * a call to __aeabi_memmove which would be slower
+ */
+ *pStateCurnt++ = *pSrc++;
+ *pStateCurnt++ = *pSrc++;
+ *pStateCurnt++ = *pSrc++;
+ *pStateCurnt++ = *pSrc++;
+
+ /* Read the first seven samples from the state buffer: x[n-numTaps], x[n-numTaps-1], x[n-numTaps-2] */
+ x0 = *px++;
+ x1 = *px++;
+ x2 = *px++;
+ x3 = *px++;
+ x4 = *px++;
+ x5 = *px++;
+ x6 = *px++;
+
+ /* Loop unrolling. Process 8 taps at a time. */
+ tapCnt = numTaps >> 3u;
+
+ /* Loop over the number of taps. Unroll by a factor of 8.
+ ** Repeat until we've computed numTaps-8 coefficients. */
+ while(tapCnt > 0u)
+ {
+ /* Read the b[numTaps-1] coefficient */
+ c0 = *(pb++);
+
+ /* Read x[n-numTaps-3] sample */
+ x7 = *(px++);
+
+ /* acc0 += b[numTaps-1] * x[n-numTaps] */
+ p0 = x0 * c0;
+
+ /* acc1 += b[numTaps-1] * x[n-numTaps-1] */
+ p1 = x1 * c0;
+
+ /* acc2 += b[numTaps-1] * x[n-numTaps-2] */
+ p2 = x2 * c0;
+
+ /* acc3 += b[numTaps-1] * x[n-numTaps-3] */
+ p3 = x3 * c0;
+
+ /* acc4 += b[numTaps-1] * x[n-numTaps-4] */
+ p4 = x4 * c0;
+
+ /* acc1 += b[numTaps-1] * x[n-numTaps-5] */
+ p5 = x5 * c0;
+
+ /* acc2 += b[numTaps-1] * x[n-numTaps-6] */
+ p6 = x6 * c0;
+
+ /* acc3 += b[numTaps-1] * x[n-numTaps-7] */
+ p7 = x7 * c0;
+
+ /* Read the b[numTaps-2] coefficient */
+ c0 = *(pb++);
+
+ /* Read x[n-numTaps-4] sample */
+ x0 = *(px++);
+
+ acc0 += p0;
+ acc1 += p1;
+ acc2 += p2;
+ acc3 += p3;
+ acc4 += p4;
+ acc5 += p5;
+ acc6 += p6;
+ acc7 += p7;
+
+
+ /* Perform the multiply-accumulate */
+ p0 = x1 * c0;
+ p1 = x2 * c0;
+ p2 = x3 * c0;
+ p3 = x4 * c0;
+ p4 = x5 * c0;
+ p5 = x6 * c0;
+ p6 = x7 * c0;
+ p7 = x0 * c0;
+
+ /* Read the b[numTaps-3] coefficient */
+ c0 = *(pb++);
+
+ /* Read x[n-numTaps-5] sample */
+ x1 = *(px++);
+
+ acc0 += p0;
+ acc1 += p1;
+ acc2 += p2;
+ acc3 += p3;
+ acc4 += p4;
+ acc5 += p5;
+ acc6 += p6;
+ acc7 += p7;
+
+ /* Perform the multiply-accumulates */
+ p0 = x2 * c0;
+ p1 = x3 * c0;
+ p2 = x4 * c0;
+ p3 = x5 * c0;
+ p4 = x6 * c0;
+ p5 = x7 * c0;
+ p6 = x0 * c0;
+ p7 = x1 * c0;
+
+ /* Read the b[numTaps-4] coefficient */
+ c0 = *(pb++);
+
+ /* Read x[n-numTaps-6] sample */
+ x2 = *(px++);
+
+ acc0 += p0;
+ acc1 += p1;
+ acc2 += p2;
+ acc3 += p3;
+ acc4 += p4;
+ acc5 += p5;
+ acc6 += p6;
+ acc7 += p7;
+
+ /* Perform the multiply-accumulates */
+ p0 = x3 * c0;
+ p1 = x4 * c0;
+ p2 = x5 * c0;
+ p3 = x6 * c0;
+ p4 = x7 * c0;
+ p5 = x0 * c0;
+ p6 = x1 * c0;
+ p7 = x2 * c0;
+
+ /* Read the b[numTaps-4] coefficient */
+ c0 = *(pb++);
+
+ /* Read x[n-numTaps-6] sample */
+ x3 = *(px++);
+
+ acc0 += p0;
+ acc1 += p1;
+ acc2 += p2;
+ acc3 += p3;
+ acc4 += p4;
+ acc5 += p5;
+ acc6 += p6;
+ acc7 += p7;
+
+ /* Perform the multiply-accumulates */
+ p0 = x4 * c0;
+ p1 = x5 * c0;
+ p2 = x6 * c0;
+ p3 = x7 * c0;
+ p4 = x0 * c0;
+ p5 = x1 * c0;
+ p6 = x2 * c0;
+ p7 = x3 * c0;
+
+ /* Read the b[numTaps-4] coefficient */
+ c0 = *(pb++);
+
+ /* Read x[n-numTaps-6] sample */
+ x4 = *(px++);
+
+ acc0 += p0;
+ acc1 += p1;
+ acc2 += p2;
+ acc3 += p3;
+ acc4 += p4;
+ acc5 += p5;
+ acc6 += p6;
+ acc7 += p7;
+
+ /* Perform the multiply-accumulates */
+ p0 = x5 * c0;
+ p1 = x6 * c0;
+ p2 = x7 * c0;
+ p3 = x0 * c0;
+ p4 = x1 * c0;
+ p5 = x2 * c0;
+ p6 = x3 * c0;
+ p7 = x4 * c0;
+
+ /* Read the b[numTaps-4] coefficient */
+ c0 = *(pb++);
+
+ /* Read x[n-numTaps-6] sample */
+ x5 = *(px++);
+
+ acc0 += p0;
+ acc1 += p1;
+ acc2 += p2;
+ acc3 += p3;
+ acc4 += p4;
+ acc5 += p5;
+ acc6 += p6;
+ acc7 += p7;
+
+ /* Perform the multiply-accumulates */
+ p0 = x6 * c0;
+ p1 = x7 * c0;
+ p2 = x0 * c0;
+ p3 = x1 * c0;
+ p4 = x2 * c0;
+ p5 = x3 * c0;
+ p6 = x4 * c0;
+ p7 = x5 * c0;
+
+ /* Read the b[numTaps-4] coefficient */
+ c0 = *(pb++);
+
+ /* Read x[n-numTaps-6] sample */
+ x6 = *(px++);
+
+ acc0 += p0;
+ acc1 += p1;
+ acc2 += p2;
+ acc3 += p3;
+ acc4 += p4;
+ acc5 += p5;
+ acc6 += p6;
+ acc7 += p7;
+
+ /* Perform the multiply-accumulates */
+ p0 = x7 * c0;
+ p1 = x0 * c0;
+ p2 = x1 * c0;
+ p3 = x2 * c0;
+ p4 = x3 * c0;
+ p5 = x4 * c0;
+ p6 = x5 * c0;
+ p7 = x6 * c0;
+
+ tapCnt--;
+
+ acc0 += p0;
+ acc1 += p1;
+ acc2 += p2;
+ acc3 += p3;
+ acc4 += p4;
+ acc5 += p5;
+ acc6 += p6;
+ acc7 += p7;
+ }
+
+ /* If the filter length is not a multiple of 8, compute the remaining filter taps */
+ tapCnt = numTaps % 0x8u;
+
+ while(tapCnt > 0u)
+ {
+ /* Read coefficients */
+ c0 = *(pb++);
+
+ /* Fetch 1 state variable */
+ x7 = *(px++);
+
+ /* Perform the multiply-accumulates */
+ p0 = x0 * c0;
+ p1 = x1 * c0;
+ p2 = x2 * c0;
+ p3 = x3 * c0;
+ p4 = x4 * c0;
+ p5 = x5 * c0;
+ p6 = x6 * c0;
+ p7 = x7 * c0;
+
+ /* Reuse the present sample states for next sample */
+ x0 = x1;
+ x1 = x2;
+ x2 = x3;
+ x3 = x4;
+ x4 = x5;
+ x5 = x6;
+ x6 = x7;
+
+ acc0 += p0;
+ acc1 += p1;
+ acc2 += p2;
+ acc3 += p3;
+ acc4 += p4;
+ acc5 += p5;
+ acc6 += p6;
+ acc7 += p7;
+
+ /* Decrement the loop counter */
+ tapCnt--;
+ }
+
+ /* Advance the state pointer by 8 to process the next group of 8 samples */
+ pState = pState + 8;
+
+ /* The results in the 8 accumulators, store in the destination buffer. */
+ *pDst++ = acc0;
+ *pDst++ = acc1;
+ *pDst++ = acc2;
+ *pDst++ = acc3;
+ *pDst++ = acc4;
+ *pDst++ = acc5;
+ *pDst++ = acc6;
+ *pDst++ = acc7;
+
+ blkCnt--;
+ }
+
+ /* If the blockSize is not a multiple of 8, compute any remaining output samples here.
+ ** No loop unrolling is used. */
+ blkCnt = blockSize % 0x8u;
+
+ while(blkCnt > 0u)
+ {
+ /* Copy one sample at a time into state buffer */
+ *pStateCurnt++ = *pSrc++;
+
+ /* Set the accumulator to zero */
+ acc0 = 0.0f;
+
+ /* Initialize state pointer */
+ px = pState;
+
+ /* Initialize Coefficient pointer */
+ pb = (pCoeffs);
+
+ i = numTaps;
+
+ /* Perform the multiply-accumulates */
+ do
+ {
+ acc0 += *px++ * *pb++;
+ i--;
+
+ } while(i > 0u);
+
+ /* The result is store in the destination buffer. */
+ *pDst++ = acc0;
+
+ /* Advance state pointer by 1 for the next sample */
+ pState = pState + 1;
+
+ blkCnt--;
+ }
+
+ /* Processing is complete.
+ ** Now copy the last numTaps - 1 samples to the start of the state buffer.
+ ** This prepares the state buffer for the next function call. */
+
+ /* Points to the start of the state buffer */
+ pStateCurnt = S->pState;
+
+ tapCnt = (numTaps - 1u) >> 2u;
+
+ /* copy data */
+ while(tapCnt > 0u)
+ {
+ *pStateCurnt++ = *pState++;
+ *pStateCurnt++ = *pState++;
+ *pStateCurnt++ = *pState++;
+ *pStateCurnt++ = *pState++;
+
+ /* Decrement the loop counter */
+ tapCnt--;
+ }
+
+ /* Calculate remaining number of copies */
+ tapCnt = (numTaps - 1u) % 0x4u;
+
+ /* Copy the remaining q31_t data */
+ while(tapCnt > 0u)
+ {
+ *pStateCurnt++ = *pState++;
+
+ /* Decrement the loop counter */
+ tapCnt--;
+ }
+}
--- /dev/null
+/**
+ * 5_fir_differentiator.cpp
+ * Written by Clyne Sullivan.
+ *
+ * Does an FIR differentiation on the incoming signal, so that the output is representative of the
+ * rate of change of the input.
+ * A scaling factor is applied so that the output's form is more clearly visible.
+ */
+
+Sample *process_data(Samples samples)
+{
+ constexpr int scaling_factor = 4;
+ static Sample output[samples.size()];
+ static Sample prev = 2048;
+
+ // Compute the first output value using the saved sample.
+ output[0] = 2048 + ((samples[0] - prev) * scaling_factor);
+
+ for (unsigned int i = 1; i < samples.size(); i++) {
+ // Take the rate of change and scale it.
+ // 2048 is added as the output should be centered in the voltage range.
+ output[i] = 2048 + ((samples[i] - samples[i - 1]) * scaling_factor);
+ }
+
+ // Save the last sample for the next iteration.
+ prev = samples[samples.size() - 1];
+
+ return output;
+}
+
--- /dev/null
+Sample *process_data(Samples samples)
+{
+ constexpr float alpha = 0.7;
+
+ static Sample prev = 2048;
+
+ samples[0] = (1 - alpha) * samples[0] + alpha * prev;
+ for (unsigned int i = 1; i < samples.size(); i++)
+ samples[i] = (1 - alpha) * samples[i] + alpha * samples[i - 1];
+ prev = samples[samples.size() - 1];
+
+ return samples.data();
+}
--- /dev/null
+Sample *process_data(Samples samples)
+{
+ constexpr float alpha = 0.75;
+ constexpr unsigned int D = 100;
+
+ static Sample output[samples.size()];
+ static Sample prev[D]; // prev[0] = output[0 - D]
+
+ // Do calculations with previous output
+ for (unsigned int i = 0; i < D; i++)
+ output[i] = samples[i] + alpha * (prev[i] - 2048);
+
+ // Do calculations with current samples
+ for (unsigned int i = D; i < samples.size(); i++)
+ output[i] = samples[i] + alpha * (output[i - D] - 2048);
+
+ // Save outputs for next computation
+ for (unsigned int i = 0; i < D; i++)
+ prev[i] = output[samples.size() - (D - i)];
+
+ return output;
+}
+/**
+ * @file circular.hpp
+ * @brief Small utility for filling a buffer in a circular manner.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
#ifndef CIRCULAR_HPP
#define CIRCULAR_HPP
+/**
+ * @file code.cpp
+ * @brief Functionality for compiling and disassembling source code.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
#include "stmdsp.hpp"
#include "stmdsp_code.hpp"
#include <string>
extern std::shared_ptr<stmdsp::device> m_device;
-extern void log(const std::string& str);
+void log(const std::string& str);
-std::string tempFileName; // device.cpp
+std::ifstream compileOpenBinaryFile();
+void compileEditorCode(const std::string& code);
+void disassembleCode();
+static std::string tempFileName;
static std::string newTempFileName();
static bool codeExecuteCommand(
const std::string& command,
const std::string& what,
const std::string& with);
+std::ifstream compileOpenBinaryFile()
+{
+ if (!tempFileName.empty())
+ return std::ifstream(tempFileName + ".o");
+ else
+ return std::ifstream();
+}
+
void compileEditorCode(const std::string& code)
{
log("Compiling...");
#include <thread>
#include <vector>
-extern std::string tempFileName;
extern void log(const std::string& str);
extern std::vector<stmdsp::dacsample_t> deviceGenLoadFormulaEval(const std::string&);
+extern std::ifstream compileOpenBinaryFile();
std::shared_ptr<stmdsp::device> m_device;
bool deviceGenStartToggle()
{
if (m_device) {
- bool running = m_device->is_siggening();
+ const bool running = m_device->is_siggening();
+
if (!running) {
- if (wavOutput.valid())
+ if (wavOutput.valid()) {
std::thread(feedSigGenTask, m_device).detach();
- else
+ } else {
+ std::scoped_lock dlock (mutexDeviceLoad);
m_device->siggen_start();
+ }
log("Generator started.");
} else {
- m_device->siggen_stop();
+ {
+ std::scoped_lock dlock (mutexDeviceLoad);
+ m_device->siggen_stop();
+ }
log("Generator stopped.");
}
log("No device connected.");
} else if (m_device->is_running()) {
log("Cannot upload algorithm while running.");
- } else if (std::ifstream algo (tempFileName + ".o"); algo.is_open()) {
+ } else if (auto algo = compileOpenBinaryFile(); algo.is_open()) {
std::ostringstream sstr;
sstr << algo.rdbuf();
auto str = sstr.str();
+/**
+ * @file device_formula.cpp
+ * @brief Function for filling generator buffer using a mathematical formula.
+ * This is kept in its own file as exprtk.hpp takes forever to compile.
+ *
+ * Copyright (C) 2021 Clyne Sullivan
+ *
+ * Distributed under the GNU GPL v3 or later. You should have received a copy of
+ * the GNU General Public License along with this program.
+ * If not, see <https://www.gnu.org/licenses/>.
+ */
+
#include "stmdsp.hpp"
#include "exprtk.hpp"
#include <algorithm>
+#include <random>
#include <string_view>
#include <vector>
+static std::random_device randomDevice;
+
std::vector<stmdsp::dacsample_t> deviceGenLoadFormulaEval(const std::string& formulaString)
{
double x = 0;
exprtk::symbol_table<double> symbol_table;
+ exprtk::function_compositor<double> compositor (symbol_table);
exprtk::expression<double> expression;
exprtk::parser<double> parser;
- symbol_table.add_variable("x", x);
symbol_table.add_constants();
+ symbol_table.add_variable("x", x);
+ symbol_table.add_function("random",
+ [](double l, double h) -> double {
+ return std::uniform_real_distribution<double>(l, h)(randomDevice);
+ });
+ compositor.add(exprtk::function_compositor<double>::function()
+ .name("square")
+ .var("X")
+ .expression("ceil(sin(pi*X))"));
+ compositor.add(exprtk::function_compositor<double>::function()
+ .name("triangle")
+ .var("X")
+ .expression("ceil(sin(pi*X))*(X-floor(X))+ceil(-sin(pi*X))*(-X-floor(-X))"));
+ compositor.add(exprtk::function_compositor<double>::function()
+ .name("pulse")
+ .var("L")
+ .var("X")
+ .expression("if(X<=L,1,0)"));
expression.register_symbol_table(symbol_table);
parser.compile(formulaString, expression);
- std::vector<stmdsp::dacsample_t> samples (stmdsp::SAMPLES_MAX);
-
- auto genFun = [&x, &expression] {
- stmdsp::dacsample_t s = expression.value();
+ const auto genFun = [&x, &expression] {
+ const auto s = std::clamp(expression.value(), -1., 1.) * 2048. + 2048.;
++x;
- return s;
+ return static_cast<stmdsp::dacsample_t>(std::min(s, 4095.));
};
+ std::vector<stmdsp::dacsample_t> samples (stmdsp::SAMPLES_MAX);
std::generate(samples.begin(), samples.end(), genFun);
return samples;
}
static FileAction fileAction = FileAction::None;
static std::string fileCurrentPath;
-static std::vector<std::filesystem::path> fileTemplateList;
+static std::vector<std::filesystem::path> fileExampleList;
static void saveCurrentFile()
{
editor.SetText(stmdsp::file_content);
}
-static std::vector<std::filesystem::path> fileScanTemplates()
+static std::vector<std::filesystem::path> fileScanExamples()
{
- const auto path = std::filesystem::current_path() / "templates";
+ const auto path = std::filesystem::current_path() / "examples";
const std::filesystem::recursive_directory_iterator rdi (path);
std::vector<std::filesystem::path> list;
void fileInit()
{
- fileTemplateList = fileScanTemplates();
+ fileExampleList = fileScanExamples();
openNewFile();
}
"ChooseFileOpenSave", "Choose File", ".cpp", ".");
}
- if (ImGui::BeginMenu("Open Template")) {
- for (const auto& file : fileTemplateList) {
+ if (ImGui::BeginMenu("Open Example")) {
+ for (const auto& file : fileExampleList) {
if (ImGui::MenuItem(file.filename().c_str())) {
fileCurrentPath = file.string();
openCurrentFile();
#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
-ImFont *fontSans = nullptr;
-ImFont *fontMono = nullptr;
-static ImGuiIO *io = nullptr;
+bool guiInitialize();
+void guiRender();
+bool guiHandleEvents();
+void guiShutdown();
+
static SDL_Window *window = nullptr;
-static decltype(SDL_GL_CreateContext(nullptr)) gl_context;
+static SDL_GLContext gl_context;
bool guiInitialize()
{
- if (SDL_Init(/*SDL_INIT_VIDEO*/0) != 0) {
+ if (SDL_Init(0) != 0) {
printf("Error: %s\n", SDL_GetError());
return false;
}
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
WINDOW_WIDTH, WINDOW_HEIGHT,
SDL_WINDOW_OPENGL /*| SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI*/);
+
+ if (window == nullptr) {
+ puts("Error: Could not create the window!");
+ return false;
+ }
+
gl_context = SDL_GL_CreateContext(window);
SDL_GL_MakeCurrent(window, gl_context);
SDL_GL_SetSwapInterval(1); // Enable vsync
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
- io = &ImGui::GetIO();
//io->ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
- fontSans = io->Fonts->AddFontFromFileTTF("fonts/Roboto-Regular.ttf", 20);
- fontMono = io->Fonts->AddFontFromFileTTF("fonts/RobotoMono-Regular.ttf", 20);
ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
ImGui_ImplOpenGL2_Init();
return true;
}
-void guiRender(void (*func)())
+void guiRender()
{
- glViewport(0, 0, (int)io->DisplaySize.x, (int)io->DisplaySize.y);
- glClearColor(1, 1, 1, 1);
- glClear(GL_COLOR_BUFFER_BIT);
- func();
- SDL_GL_SwapWindow(window);
+ ImGui::Render();
+
+ const auto& displaySize = ImGui::GetIO().DisplaySize;
+ const int sizeX = static_cast<int>(displaySize.x);
+ const int sizeY = static_cast<int>(displaySize.y);
+
+ glViewport(0, 0, sizeX, sizeY);
+ glClearColor(1, 1, 1, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+ ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData());
+ SDL_GL_SwapWindow(window);
}
bool guiHandleEvents()
for (SDL_Event event; SDL_PollEvent(&event);) {
ImGui_ImplSDL2_ProcessEvent(&event);
- if (event.type == SDL_QUIT)
- done = true;
- if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window))
+ if (event.type == SDL_QUIT) {
done = true;
+ } else if (event.type == SDL_WINDOWEVENT) {
+ const auto& ew = event.window;
+ const auto wid = SDL_GetWindowID(window);
+ if (ew.event == SDL_WINDOWEVENT_CLOSE && ew.windowID == wid)
+ done = true;
+ }
}
return done;
void codeRenderMenu()
{
if (ImGui::BeginMenu("Code")) {
- if (ImGui::MenuItem("Compile code"))
+ if (ImGui::MenuItem("Compile"))
codeCompile();
- if (ImGui::MenuItem("Show disassembly"))
+ if (ImGui::MenuItem("Disassemble"))
codeDisassemble();
ImGui::EndMenu();
}
};
- if (ImGui::BeginMenu("Run")) {
+ if (ImGui::BeginMenu("Device")) {
static std::string connectLabel ("Connect");
addMenuItem(connectLabel, !m_device || !m_device->is_running(), [&] {
if (deviceConnect()) {
ImGui::PopStyleColor();
}
- if (ImGui::Button("Cancel")) {
- siggenInput.clear();
- ImGui::CloseCurrentPopup();
- }
-
if (ImGui::Button("Save")) {
switch (siggenOption) {
case 0:
ImGui::CloseCurrentPopup();
}
+ ImGui::SameLine();
+ if (ImGui::Button("Cancel")) {
+ siggenInput.clear();
+ ImGui::CloseCurrentPopup();
+ }
+
ImGui::EndPopup();
}
if (mouse.x > p0.x && mouse.x < p0.x + size.x &&
mouse.y > p0.y && mouse.y < p0.y + size.y)
{
- const std::size_t si = (mouse.x - p0.x) / size.x * buffer.size();
- const float s = buffer[si] / 4095.f * 6.6f - 3.3f;
- char buf[12];
- snprintf(buf, 16, " %1.3fV", s);
+ char buf[16];
drawList->AddLine({mouse.x, p0.y}, {mouse.x, p0.y + size.y}, IM_COL32(255, 255, 0, 255));
- drawList->AddText(ImGui::GetMousePos(), IM_COL32(210, 210, 0, 255), buf);
+
+ {
+ const std::size_t si = (mouse.x - p0.x) / size.x * buffer.size();
+ const float s = buffer[si] / 4095.f * 6.6f - 3.3f;
+ snprintf(buf, sizeof(buf), " %1.3fV", s);
+ drawList->AddText(mouse, IM_COL32(255, 0, 0, 255), buf);
+ }
+
+ if (drawSamplesInput) {
+ const std::size_t si = (mouse.x - p0.x) / size.x * bufferInput.size();
+ const float s = bufferInput[si] / 4095.f * 6.6f - 3.3f;
+ snprintf(buf, sizeof(buf), " %1.3fV", s);
+ drawList->AddText({mouse.x, mouse.y + 20}, IM_COL32(0, 0, 255, 255), buf);
+ }
}
ImGui::End();
, mHandleKeyboardInputs(true)
, mHandleMouseInputs(true)
, mIgnoreImGuiChild(false)
- , mShowWhitespaces(true)
+ , mShowWhitespaces(false)
, mStartTime(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count())
{
SetPalette(GetDarkPalette());
}
ImGui::Text("Log ");
- ImGui::SameLine();
+ ImGui::SameLine(ImGui::GetWindowWidth() - 120);
if (ImGui::Button("Clear"))
Clear();
ImGui::SameLine();
#include <chrono>
#include <cmath>
+#include <iostream>
#include <string>
#include <thread>
-extern ImFont *fontSans;
-extern ImFont *fontMono;
-
-bool guiInitialize();
-bool guiHandleEvents();
-void guiShutdown();
-void guiRender(void (*func)());
-
-void fileRenderMenu();
-void fileRenderDialog();
-void fileInit();
-
void codeEditorInit();
void codeRenderMenu();
void codeRenderToolbar();
void codeRenderWidgets();
-
void deviceRenderDraw();
void deviceRenderMenu();
void deviceRenderToolbar();
void deviceRenderWidgets();
+void fileRenderMenu();
+void fileRenderDialog();
+void fileInit();
+bool guiInitialize();
+bool guiHandleEvents();
+void guiShutdown();
+void guiRender();
-static LogView logView;
+void log(const std::string& str);
-void log(const std::string& str)
-{
- logView.AddLog(str);
-}
+static LogView logView;
+static ImFont *fontSans = nullptr;
+static ImFont *fontMono = nullptr;
static void renderWindow();
if (!guiInitialize())
return -1;
+ auto& io = ImGui::GetIO();
+ fontSans = io.Fonts->AddFontFromFileTTF("fonts/Roboto-Regular.ttf", 20);
+ fontMono = io.Fonts->AddFontFromFileTTF("fonts/RobotoMono-Regular.ttf", 20);
+ if (fontSans == nullptr || fontMono == nullptr) {
+ std::cout << "Failed to load fonts!" << std::endl;
+ return -1;
+ }
+
codeEditorInit();
fileInit();
return 0;
}
+void log(const std::string& str)
+{
+ logView.AddLog(str);
+}
+
void renderWindow()
{
// Start the new window frame and render the menu bar.
deviceRenderDraw();
// Draw everything to the screen.
- ImGui::Render();
- guiRender([] {
- ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData());
- });
+ guiRender();
}
+++ /dev/null
-/**
- * 1_convolve_simple.cpp
- * Written by Clyne Sullivan.
- *
- * Computes a convolution in the simplest way possible. While the code is brief, it lacks many
- * possible optimizations. The convolution's result will not fill the output buffer either, as the
- * transient response is not calculated.
- */
-
-Sample *process_data(Samples samples)
-{
- // Define our output buffer. SIZE is the largest size of the 'samples' buffer.
- static Sample buffer[samples.size()];
-
- // Define our filter
- constexpr unsigned int filter_size = 3;
- float filter[filter_size] = {
- 0.3333, 0.3333, 0.3333
- };
-
- // Begin convolving:
- for (int n = 0; n < samples.size() - (filter_size - 1); n++) {
- buffer[n] = 0;
- for (int k = 0; k < filter_size; k++)
- buffer[n] += samples[n + k] * filter[k];
- }
-
- return buffer;
-}
+++ /dev/null
-/**
- * 2_convolve_overlap_save.cpp
- * Written by Clyne Sullivan.
- *
- * This convolution examples takes an overlap-save approach, where samples from the previous run
- * are saved so that the overall operation is not interrupted (i.e. the observed output will
- * transition smoothly between processed "chunks").
- *
- * Note that there are still improvements that can be made to the code; for example, notice every
- * spot where an integer/float conversion is necessary. Operations like these may slow down the
- * computation.
- */
-
-Sample *process_data(Samples samples)
-{
- static Sample buffer[samples.size()];
-
- constexpr unsigned int filter_size = 3;
- float filter[filter_size] = {
- 0.3333, 0.3333, 0.3333
- };
-
- // Keep a buffer of extra samples for overlap-save
- static Sample prev[filter_size];
-
- for (int n = 0; n < samples.size(); n++) {
- buffer[n] = 0;
-
- for (int k = 0; k < filter_size; k++) {
- int i = n - (filter_size - 1) + k;
-
- // If i is >= 0, access current sample buffer.
- // If i is < 0, provide the previous samples from the 'prev' buffer
- if (i >= 0)
- buffer[n] += samples[i] * filter[k];
- else
- buffer[n] += prev[filter_size - 1 + i] * filter[k];
- }
- }
-
- // Save samples for the next convolution run
- for (int i = 0; i < filter_size; i++)
- prev[i] = samples[samples.size() - filter_size + i];
-
- return buffer;
-}
-
+++ /dev/null
-/**
- * 3_fir.cpp
- * Written by Clyne Sullivan.
- *
- * The below code was written for applying FIR filters. While this is still essentially an overlap-
- * save convolution, other optimizations have been made to allow for larger filters to be applied
- * within the available execution time. Samples are also normalized so that they center around zero.
- */
-
-Sample *process_data(Samples samples)
-{
- static Sample buffer[samples.size()];
-
- // Define the filter:
- constexpr unsigned int filter_size = 3;
- static float filter[filter_size] = {
- // Put filter values here (note: precision will be truncated for 'float' size).
- 0.3333, 0.3333, 0.3333
- };
-
- // Do an overlap-save convolution
- static Sample prev[filter_size];
-
- for (int n = 0; n < samples.size(); n++) {
- // Using a float variable for accumulation allows for better code optimization
- float v = 0;
-
- for (int k = 0; k < filter_size; k++) {
- int i = n - (filter_size - 1) + k;
-
- auto s = i >= 0 ? samples[i] : prev[filter_size - 1 + i];
- // Sample values are 0 to 4095. Below, the original sample is normalized to a -1.0 to
- // 1.0 range for calculation.
- v += (s / 2048.f - 1) * filter[k];
- }
-
- // Return value to sample range of 0-4095.
- buffer[n] = (v + 1) * 2048.f;
- }
-
- // Save samples for next convolution
- for (int i = 0; i < filter_size; i++)
- prev[i] = samples[samples.size() - filter_size + i];
-
- return buffer;
-}
-
+++ /dev/null
-#include <cstdint>
-using float32_t = float;
-
-typedef struct
-{
- uint16_t numTaps; /**< number of filter coefficients in the filter. */
- float32_t *pState; /**< points to the state variable array. The array is of length numTaps+blockSize-1. */
- float32_t *pCoeffs; /**< points to the coefficient array. The array is of length numTaps. */
-} arm_fir_instance_f32;
-
-static void arm_fir_f32(const arm_fir_instance_f32 * S, float32_t * pSrc, float32_t * pDst, uint32_t blockSize);
-
-Sample *process_data(Samples samples)
-{
- // 1. Define our array sizes (Be sure to set Run > Set buffer size... to below value!)
- constexpr unsigned int buffer_size = 500;
- constexpr unsigned int filter_size = 100;
-
- // 2. Define our filter and the working arrays
- static float filter[filter_size] = {
- .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
- .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
- .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
- .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
- .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
- .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
- .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
- .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
- .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,
- .01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f,.01f
- };
- static float input[buffer_size];
- static float output[buffer_size];
- static float working[buffer_size + filter_size];
-
- // 3. Scale 0-4095 interger sample values to +/- 1.0 floats
- for (unsigned int i = 0; i < samples.size(); i++)
- input[i] = (samples[i] - 2048) / 2048.f;
-
- // 4. Compute the FIR
- arm_fir_instance_f32 fir { filter_size, working, filter };
- arm_fir_f32(&fir, input, output, samples.size());
-
- // 5. Convert float results back to 0-4095 range for output
- for (unsigned int i = 0; i < samples.size(); i++)
- samples[i] = output[i] * 2048.f + 2048;
-
- return samples.data();
-}
-
-// Below taken from the CMSIS DSP Library (find it on GitHub)
-void arm_fir_f32(
- const arm_fir_instance_f32 * S,
- float32_t * pSrc,
- float32_t * pDst,
- uint32_t blockSize)
-{
- float32_t *pState = S->pState; /* State pointer */
- float32_t *pCoeffs = S->pCoeffs; /* Coefficient pointer */
- float32_t *pStateCurnt; /* Points to the current sample of the state */
- float32_t *px, *pb; /* Temporary pointers for state and coefficient buffers */
- float32_t acc0, acc1, acc2, acc3, acc4, acc5, acc6, acc7; /* Accumulators */
- float32_t x0, x1, x2, x3, x4, x5, x6, x7, c0; /* Temporary variables to hold state and coefficient values */
- uint32_t numTaps = S->numTaps; /* Number of filter coefficients in the filter */
- uint32_t i, tapCnt, blkCnt; /* Loop counters */
- float32_t p0,p1,p2,p3,p4,p5,p6,p7; /* Temporary product values */
-
- /* S->pState points to state array which contains previous frame (numTaps - 1) samples */
- /* pStateCurnt points to the location where the new input data should be written */
- pStateCurnt = &(S->pState[(numTaps - 1u)]);
-
- /* Apply loop unrolling and compute 8 output values simultaneously.
- * The variables acc0 ... acc7 hold output values that are being computed:
- *
- * acc0 = b[numTaps-1] * x[n-numTaps-1] + b[numTaps-2] * x[n-numTaps-2] + b[numTaps-3] * x[n-numTaps-3] +...+ b[0] * x[0]
- * acc1 = b[numTaps-1] * x[n-numTaps] + b[numTaps-2] * x[n-numTaps-1] + b[numTaps-3] * x[n-numTaps-2] +...+ b[0] * x[1]
- * acc2 = b[numTaps-1] * x[n-numTaps+1] + b[numTaps-2] * x[n-numTaps] + b[numTaps-3] * x[n-numTaps-1] +...+ b[0] * x[2]
- * acc3 = b[numTaps-1] * x[n-numTaps+2] + b[numTaps-2] * x[n-numTaps+1] + b[numTaps-3] * x[n-numTaps] +...+ b[0] * x[3]
- */
- blkCnt = blockSize >> 3;
-
- /* First part of the processing with loop unrolling. Compute 8 outputs at a time.
- ** a second loop below computes the remaining 1 to 7 samples. */
- while(blkCnt > 0u)
- {
- /* Copy four new input samples into the state buffer */
- *pStateCurnt++ = *pSrc++;
- *pStateCurnt++ = *pSrc++;
- *pStateCurnt++ = *pSrc++;
- *pStateCurnt++ = *pSrc++;
-
- /* Set all accumulators to zero */
- acc0 = 0.0f;
- acc1 = 0.0f;
- acc2 = 0.0f;
- acc3 = 0.0f;
- acc4 = 0.0f;
- acc5 = 0.0f;
- acc6 = 0.0f;
- acc7 = 0.0f;
-
- /* Initialize state pointer */
- px = pState;
-
- /* Initialize coeff pointer */
- pb = (pCoeffs);
-
- /* This is separated from the others to avoid
- * a call to __aeabi_memmove which would be slower
- */
- *pStateCurnt++ = *pSrc++;
- *pStateCurnt++ = *pSrc++;
- *pStateCurnt++ = *pSrc++;
- *pStateCurnt++ = *pSrc++;
-
- /* Read the first seven samples from the state buffer: x[n-numTaps], x[n-numTaps-1], x[n-numTaps-2] */
- x0 = *px++;
- x1 = *px++;
- x2 = *px++;
- x3 = *px++;
- x4 = *px++;
- x5 = *px++;
- x6 = *px++;
-
- /* Loop unrolling. Process 8 taps at a time. */
- tapCnt = numTaps >> 3u;
-
- /* Loop over the number of taps. Unroll by a factor of 8.
- ** Repeat until we've computed numTaps-8 coefficients. */
- while(tapCnt > 0u)
- {
- /* Read the b[numTaps-1] coefficient */
- c0 = *(pb++);
-
- /* Read x[n-numTaps-3] sample */
- x7 = *(px++);
-
- /* acc0 += b[numTaps-1] * x[n-numTaps] */
- p0 = x0 * c0;
-
- /* acc1 += b[numTaps-1] * x[n-numTaps-1] */
- p1 = x1 * c0;
-
- /* acc2 += b[numTaps-1] * x[n-numTaps-2] */
- p2 = x2 * c0;
-
- /* acc3 += b[numTaps-1] * x[n-numTaps-3] */
- p3 = x3 * c0;
-
- /* acc4 += b[numTaps-1] * x[n-numTaps-4] */
- p4 = x4 * c0;
-
- /* acc1 += b[numTaps-1] * x[n-numTaps-5] */
- p5 = x5 * c0;
-
- /* acc2 += b[numTaps-1] * x[n-numTaps-6] */
- p6 = x6 * c0;
-
- /* acc3 += b[numTaps-1] * x[n-numTaps-7] */
- p7 = x7 * c0;
-
- /* Read the b[numTaps-2] coefficient */
- c0 = *(pb++);
-
- /* Read x[n-numTaps-4] sample */
- x0 = *(px++);
-
- acc0 += p0;
- acc1 += p1;
- acc2 += p2;
- acc3 += p3;
- acc4 += p4;
- acc5 += p5;
- acc6 += p6;
- acc7 += p7;
-
-
- /* Perform the multiply-accumulate */
- p0 = x1 * c0;
- p1 = x2 * c0;
- p2 = x3 * c0;
- p3 = x4 * c0;
- p4 = x5 * c0;
- p5 = x6 * c0;
- p6 = x7 * c0;
- p7 = x0 * c0;
-
- /* Read the b[numTaps-3] coefficient */
- c0 = *(pb++);
-
- /* Read x[n-numTaps-5] sample */
- x1 = *(px++);
-
- acc0 += p0;
- acc1 += p1;
- acc2 += p2;
- acc3 += p3;
- acc4 += p4;
- acc5 += p5;
- acc6 += p6;
- acc7 += p7;
-
- /* Perform the multiply-accumulates */
- p0 = x2 * c0;
- p1 = x3 * c0;
- p2 = x4 * c0;
- p3 = x5 * c0;
- p4 = x6 * c0;
- p5 = x7 * c0;
- p6 = x0 * c0;
- p7 = x1 * c0;
-
- /* Read the b[numTaps-4] coefficient */
- c0 = *(pb++);
-
- /* Read x[n-numTaps-6] sample */
- x2 = *(px++);
-
- acc0 += p0;
- acc1 += p1;
- acc2 += p2;
- acc3 += p3;
- acc4 += p4;
- acc5 += p5;
- acc6 += p6;
- acc7 += p7;
-
- /* Perform the multiply-accumulates */
- p0 = x3 * c0;
- p1 = x4 * c0;
- p2 = x5 * c0;
- p3 = x6 * c0;
- p4 = x7 * c0;
- p5 = x0 * c0;
- p6 = x1 * c0;
- p7 = x2 * c0;
-
- /* Read the b[numTaps-4] coefficient */
- c0 = *(pb++);
-
- /* Read x[n-numTaps-6] sample */
- x3 = *(px++);
-
- acc0 += p0;
- acc1 += p1;
- acc2 += p2;
- acc3 += p3;
- acc4 += p4;
- acc5 += p5;
- acc6 += p6;
- acc7 += p7;
-
- /* Perform the multiply-accumulates */
- p0 = x4 * c0;
- p1 = x5 * c0;
- p2 = x6 * c0;
- p3 = x7 * c0;
- p4 = x0 * c0;
- p5 = x1 * c0;
- p6 = x2 * c0;
- p7 = x3 * c0;
-
- /* Read the b[numTaps-4] coefficient */
- c0 = *(pb++);
-
- /* Read x[n-numTaps-6] sample */
- x4 = *(px++);
-
- acc0 += p0;
- acc1 += p1;
- acc2 += p2;
- acc3 += p3;
- acc4 += p4;
- acc5 += p5;
- acc6 += p6;
- acc7 += p7;
-
- /* Perform the multiply-accumulates */
- p0 = x5 * c0;
- p1 = x6 * c0;
- p2 = x7 * c0;
- p3 = x0 * c0;
- p4 = x1 * c0;
- p5 = x2 * c0;
- p6 = x3 * c0;
- p7 = x4 * c0;
-
- /* Read the b[numTaps-4] coefficient */
- c0 = *(pb++);
-
- /* Read x[n-numTaps-6] sample */
- x5 = *(px++);
-
- acc0 += p0;
- acc1 += p1;
- acc2 += p2;
- acc3 += p3;
- acc4 += p4;
- acc5 += p5;
- acc6 += p6;
- acc7 += p7;
-
- /* Perform the multiply-accumulates */
- p0 = x6 * c0;
- p1 = x7 * c0;
- p2 = x0 * c0;
- p3 = x1 * c0;
- p4 = x2 * c0;
- p5 = x3 * c0;
- p6 = x4 * c0;
- p7 = x5 * c0;
-
- /* Read the b[numTaps-4] coefficient */
- c0 = *(pb++);
-
- /* Read x[n-numTaps-6] sample */
- x6 = *(px++);
-
- acc0 += p0;
- acc1 += p1;
- acc2 += p2;
- acc3 += p3;
- acc4 += p4;
- acc5 += p5;
- acc6 += p6;
- acc7 += p7;
-
- /* Perform the multiply-accumulates */
- p0 = x7 * c0;
- p1 = x0 * c0;
- p2 = x1 * c0;
- p3 = x2 * c0;
- p4 = x3 * c0;
- p5 = x4 * c0;
- p6 = x5 * c0;
- p7 = x6 * c0;
-
- tapCnt--;
-
- acc0 += p0;
- acc1 += p1;
- acc2 += p2;
- acc3 += p3;
- acc4 += p4;
- acc5 += p5;
- acc6 += p6;
- acc7 += p7;
- }
-
- /* If the filter length is not a multiple of 8, compute the remaining filter taps */
- tapCnt = numTaps % 0x8u;
-
- while(tapCnt > 0u)
- {
- /* Read coefficients */
- c0 = *(pb++);
-
- /* Fetch 1 state variable */
- x7 = *(px++);
-
- /* Perform the multiply-accumulates */
- p0 = x0 * c0;
- p1 = x1 * c0;
- p2 = x2 * c0;
- p3 = x3 * c0;
- p4 = x4 * c0;
- p5 = x5 * c0;
- p6 = x6 * c0;
- p7 = x7 * c0;
-
- /* Reuse the present sample states for next sample */
- x0 = x1;
- x1 = x2;
- x2 = x3;
- x3 = x4;
- x4 = x5;
- x5 = x6;
- x6 = x7;
-
- acc0 += p0;
- acc1 += p1;
- acc2 += p2;
- acc3 += p3;
- acc4 += p4;
- acc5 += p5;
- acc6 += p6;
- acc7 += p7;
-
- /* Decrement the loop counter */
- tapCnt--;
- }
-
- /* Advance the state pointer by 8 to process the next group of 8 samples */
- pState = pState + 8;
-
- /* The results in the 8 accumulators, store in the destination buffer. */
- *pDst++ = acc0;
- *pDst++ = acc1;
- *pDst++ = acc2;
- *pDst++ = acc3;
- *pDst++ = acc4;
- *pDst++ = acc5;
- *pDst++ = acc6;
- *pDst++ = acc7;
-
- blkCnt--;
- }
-
- /* If the blockSize is not a multiple of 8, compute any remaining output samples here.
- ** No loop unrolling is used. */
- blkCnt = blockSize % 0x8u;
-
- while(blkCnt > 0u)
- {
- /* Copy one sample at a time into state buffer */
- *pStateCurnt++ = *pSrc++;
-
- /* Set the accumulator to zero */
- acc0 = 0.0f;
-
- /* Initialize state pointer */
- px = pState;
-
- /* Initialize Coefficient pointer */
- pb = (pCoeffs);
-
- i = numTaps;
-
- /* Perform the multiply-accumulates */
- do
- {
- acc0 += *px++ * *pb++;
- i--;
-
- } while(i > 0u);
-
- /* The result is store in the destination buffer. */
- *pDst++ = acc0;
-
- /* Advance state pointer by 1 for the next sample */
- pState = pState + 1;
-
- blkCnt--;
- }
-
- /* Processing is complete.
- ** Now copy the last numTaps - 1 samples to the start of the state buffer.
- ** This prepares the state buffer for the next function call. */
-
- /* Points to the start of the state buffer */
- pStateCurnt = S->pState;
-
- tapCnt = (numTaps - 1u) >> 2u;
-
- /* copy data */
- while(tapCnt > 0u)
- {
- *pStateCurnt++ = *pState++;
- *pStateCurnt++ = *pState++;
- *pStateCurnt++ = *pState++;
- *pStateCurnt++ = *pState++;
-
- /* Decrement the loop counter */
- tapCnt--;
- }
-
- /* Calculate remaining number of copies */
- tapCnt = (numTaps - 1u) % 0x4u;
-
- /* Copy the remaining q31_t data */
- while(tapCnt > 0u)
- {
- *pStateCurnt++ = *pState++;
-
- /* Decrement the loop counter */
- tapCnt--;
- }
-}
+++ /dev/null
-/**
- * 5_fir_differentiator.cpp
- * Written by Clyne Sullivan.
- *
- * Does an FIR differentiation on the incoming signal, so that the output is representative of the
- * rate of change of the input.
- * A scaling factor is applied so that the output's form is more clearly visible.
- */
-
-Sample *process_data(Samples samples)
-{
- constexpr int scaling_factor = 4;
- static Sample output[samples.size()];
- static Sample prev = 2048;
-
- // Compute the first output value using the saved sample.
- output[0] = 2048 + ((samples[0] - prev) * scaling_factor);
-
- for (unsigned int i = 1; i < samples.size(); i++) {
- // Take the rate of change and scale it.
- // 2048 is added as the output should be centered in the voltage range.
- output[i] = 2048 + ((samples[i] - samples[i - 1]) * scaling_factor);
- }
-
- // Save the last sample for the next iteration.
- prev = samples[samples.size() - 1];
-
- return output;
-}
-
+++ /dev/null
-Sample *process_data(Samples samples)
-{
- constexpr float alpha = 0.7;
-
- static Sample prev = 2048;
-
- samples[0] = (1 - alpha) * samples[0] + alpha * prev;
- for (unsigned int i = 1; i < samples.size(); i++)
- samples[i] = (1 - alpha) * samples[i] + alpha * samples[i - 1];
- prev = samples[samples.size() - 1];
-
- return samples.data();
-}
+++ /dev/null
-Sample *process_data(Samples samples)
-{
- constexpr float alpha = 0.75;
- constexpr unsigned int D = 100;
-
- static Sample output[samples.size()];
- static Sample prev[D]; // prev[0] = output[0 - D]
-
- // Do calculations with previous output
- for (unsigned int i = 0; i < D; i++)
- output[i] = samples[i] + alpha * (prev[i] - 2048);
-
- // Do calculations with current samples
- for (unsigned int i = D; i < samples.size(); i++)
- output[i] = samples[i] + alpha * (output[i - D] - 2048);
-
- // Save outputs for next computation
- for (unsigned int i = 0; i < D; i++)
- prev[i] = output[samples.size() - (D - i)];
-
- return output;
-}