From 146247c7df014dd923f6321d79707c3025343bc2 Mon Sep 17 00:00:00 2001
From: Marek Blok <Marek.Blok@pg.edu.pl>
Date: Fri, 10 Nov 2023 21:55:40 +0100
Subject: [PATCH] Added AdjustableDelay

---
 CHANGELOG                 |  13 +-
 src/cpp/DSP_modules.cpp   | 295 ++++++++++++++++++++++++++++++++++++++
 src/cpp/DSP_modules2.cpp  |   4 +-
 src/include/DSP_lib.h     |  10 +-
 src/include/DSP_modules.h |  57 ++++++++
 5 files changed, 367 insertions(+), 12 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 51ba9a6..af14309 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
 TODO::
+- Add example with test of DSP::u::AdjustableDelay
+  + output difference between standard and cyclic buffer to check if both variants work the same
 - DSP::u::Zeroinserter => add variant with possibility to define number of inputs
-- ADD: DSP::u::ControledDelay(max_delay, initial_delay, ...)
 
   SOUND_support_t::get_wave_in_raw_buffer - zastąpić metodą zawierającą konwersję typu, ew. rozbudować konwersję w DSP::u::AudioInput
   - test socket support on Linux (test examples as there are have been noticed some problems with bind)
@@ -11,10 +12,12 @@ TODO::
 LAST DONE:
 
 CHANGES:
-- ver. 0.20.029
-  - added: DSP::Block::DefineStandardInputs
-  - added: DSP::Component::DefineStandardOutputs
-  - added: DSP::u::RawDecimator - added variant with AreInputsComplex parameter
+- ver. 0.20.030 <b>2023.11.10</b> Added:
+  - DSP::u::AdjustableDelay - delay block with programaticly controlable delay (betwee 0 and user defined max_delay).
+- ver. 0.20.029 <b>2023.11.10</b> Added:
+  - DSP::Block::DefineStandardInputs
+  - DSP::Component::DefineStandardOutputs
+  - DSP::u::RawDecimator - new variant with AreInputsComplex parameter
 - ver. 0.20.028 - <b>2023.06.14</b> Fixed: 
   - fixed gray coding in DSP::u::SymbolMapper 
 - ver. 0.20.027 - <b>2023.04.21</b> Fixed: 
diff --git a/src/cpp/DSP_modules.cpp b/src/cpp/DSP_modules.cpp
index 99054b6..08e097f 100644
--- a/src/cpp/DSP_modules.cpp
+++ b/src/cpp/DSP_modules.cpp
@@ -3621,6 +3621,301 @@ void DSP::u::Delay::InputExecute_with_cyclic_buffer_multi(INPUT_EXECUTE_ARGS)
 }
 #undef THIS
 
+
+// Adjustable delay element implemented in processing mode
+// cannot separate processing in digital feedback loop !!!
+DSP::u::AdjustableDelay::AdjustableDelay(unsigned int max_delay_in, unsigned int initial_delay, unsigned int InputsNo, bool IsBufferCyclic) : DSP::Block()
+{
+  unsigned int ind;
+  std::string temp;
+  std::vector<unsigned int> indexes;
+
+  if (IsBufferCyclic == true)
+    SetName("Delay(cyclic)", false);
+  else
+    SetName("Delay", false);
+
+  if (InputsNo <= 0)
+    InputsNo = 1;
+  SetNoOfOutputs(InputsNo);
+  SetNoOfInputs(InputsNo, false);
+
+  DefineStandardInputs(false);
+  DefineStandardOutputs(false);
+
+  ClockGroups.AddInputs2Group("all", 0, NoOfInputs-1);
+  ClockGroups.AddOutputs2Group("all", 0, NoOfOutputs-1);
+
+  NoOfInputsProcessed=InitialNoOfInputsProcessed;
+
+  max_delay=max_delay_in;
+  new_delay = initial_delay;
+  current_delay = new_delay;
+
+  if (max_delay > 0)
+  {
+    State = new DSP::Float_ptr[NoOfInputs];
+    index = new unsigned int[NoOfInputs];
+    for (ind = 0; ind < NoOfInputs; ind++)
+    {
+      State[ind]=new DSP::Float[max_delay];
+      memset(State[ind], 0, sizeof(DSP::Float) * max_delay);
+      index[ind]=0;
+    }
+  }
+  else
+  {
+    State = NULL;
+    index = NULL;
+  }
+
+  if (InputsNo == 1)
+  {
+    Execute_ptr = &InputExecute;
+    if (IsBufferCyclic == true)
+      Execute_ptr = &InputExecute_with_cyclic_buffer;
+    if (max_delay == 0)
+      Execute_ptr = &InputExecute_D0;
+    if (max_delay == 1)
+      Execute_ptr = &InputExecute_D1;
+  }
+  else
+  {
+    Execute_ptr = &InputExecute_multi;
+    if (IsBufferCyclic == true)
+      Execute_ptr = &InputExecute_with_cyclic_buffer_multi;
+    if (max_delay == 0)
+      Execute_ptr = &InputExecute_D0_multi;
+    if (max_delay == 1)
+      Execute_ptr = &InputExecute_D1_multi;
+  }
+}
+
+DSP::u::AdjustableDelay::~AdjustableDelay(void)
+{
+  unsigned int ind;
+
+//  SetNoOfOutputs(0);
+  if (State != NULL)
+  {
+    for (ind = 0; ind < NoOfInputs; ind++)
+    {
+      if (State[ind] != NULL)
+      {
+        delete [] State[ind];
+        State[ind] = NULL;
+      }
+    }
+    delete [] State;
+    State = NULL;
+  }
+  if (index != NULL)
+  {
+    delete [] index;
+    index = NULL;
+  }
+}
+
+void DSP::u::AdjustableDelay::UpdateDelayIfNeeded(void) {
+  if (NoOfInputsProcessed == InitialNoOfInputsProcessed) {
+    current_delay = new_delay;
+  }
+  NoOfInputsProcessed++;
+
+  if (NoOfInputsProcessed == NoOfInputs) {
+    NoOfInputsProcessed = InitialNoOfInputsProcessed;
+  }
+}
+
+// Sets new value of the delay.
+/* Returns actually set delay (will differ from new_delay it is is out of range)
+*/
+unsigned int DSP::u::AdjustableDelay::SetDelay(unsigned int new_delay_in) {
+  if (new_delay_in <= max_delay)
+    new_delay = new_delay_in;
+  return new_delay;
+}
+// Returns current delay
+unsigned int DSP::u::AdjustableDelay::GetCurrentDelay(void) {
+  return current_delay;
+}
+
+#define  THIS  ((DSP::u::AdjustableDelay *)block)
+void DSP::u::AdjustableDelay::InputExecute_D0(INPUT_EXECUTE_ARGS)
+{
+  UNUSED_ARGUMENT(InputNo);
+  UNUSED_DEBUG_ARGUMENT(Caller);
+
+  // \note The delay cannot be adjusted
+  THIS->UpdateDelayIfNeeded();
+
+  THIS->OutputBlocks[0]->EXECUTE_PTR(
+      THIS->OutputBlocks[0], THIS->OutputBlocks_InputNo[0],
+      value, block);
+};
+
+void DSP::u::AdjustableDelay::InputExecute_D0_multi(INPUT_EXECUTE_ARGS)
+{
+  UNUSED_DEBUG_ARGUMENT(Caller);
+
+  // \note The delay cannot be adjusted
+  THIS->UpdateDelayIfNeeded();
+
+  THIS->OutputBlocks[InputNo]->EXECUTE_PTR(
+      THIS->OutputBlocks[InputNo], THIS->OutputBlocks_InputNo[InputNo],
+      value, block);
+};
+
+void DSP::u::AdjustableDelay::InputExecute_D1(INPUT_EXECUTE_ARGS)
+{
+  UNUSED_ARGUMENT(InputNo);
+  UNUSED_DEBUG_ARGUMENT(Caller);
+
+  THIS->UpdateDelayIfNeeded();
+
+  if (THIS->current_delay == 0) {
+    THIS->OutputBlocks[0]->EXECUTE_PTR(
+        THIS->OutputBlocks[0], THIS->OutputBlocks_InputNo[0],
+        value, block);
+  }
+  else {
+    THIS->OutputBlocks[0]->EXECUTE_PTR(
+        THIS->OutputBlocks[0], THIS->OutputBlocks_InputNo[0],
+        *(THIS->State[0]), block);
+  }
+
+  // update buffer
+  *(THIS->State[0])=value;
+};
+
+void DSP::u::AdjustableDelay::InputExecute_D1_multi(INPUT_EXECUTE_ARGS)
+{
+  UNUSED_DEBUG_ARGUMENT(Caller);
+
+  THIS->UpdateDelayIfNeeded();
+
+  if (THIS->current_delay == 0) {
+    THIS->OutputBlocks[InputNo]->EXECUTE_PTR(
+        THIS->OutputBlocks[InputNo], THIS->OutputBlocks_InputNo[InputNo],
+        value, block);
+  }
+  else {
+    THIS->OutputBlocks[InputNo]->EXECUTE_PTR(
+        THIS->OutputBlocks[InputNo], THIS->OutputBlocks_InputNo[InputNo],
+        *(THIS->State[InputNo]), block);
+  }
+
+  // update buffer
+  *(THIS->State[InputNo])=value;
+};
+
+void DSP::u::AdjustableDelay::InputExecute(INPUT_EXECUTE_ARGS)
+{
+  UNUSED_ARGUMENT(InputNo);
+  UNUSED_DEBUG_ARGUMENT(Caller);
+
+  THIS->UpdateDelayIfNeeded();
+
+  if (THIS->current_delay == 0) {
+    THIS->OutputBlocks[0]->EXECUTE_PTR(
+        THIS->OutputBlocks[0], THIS->OutputBlocks_InputNo[0],
+        value, block);
+  }
+  else {
+    THIS->OutputBlocks[0]->EXECUTE_PTR(
+        THIS->OutputBlocks[0], THIS->OutputBlocks_InputNo[0],
+        THIS->State[0][THIS->max_delay - THIS->current_delay], block);
+  }
+
+  // update buffer
+  memcpy(THIS->State[0], THIS->State[0]+1, sizeof(DSP::Float)*(THIS->max_delay-1));
+  THIS->State[0][THIS->max_delay-1]=value;
+};
+
+void DSP::u::AdjustableDelay::InputExecute_multi(INPUT_EXECUTE_ARGS)
+{
+  UNUSED_DEBUG_ARGUMENT(Caller);
+
+  THIS->UpdateDelayIfNeeded();
+
+  if (THIS->current_delay == 0) {
+    THIS->OutputBlocks[InputNo]->EXECUTE_PTR(
+        THIS->OutputBlocks[InputNo], THIS->OutputBlocks_InputNo[InputNo],
+        value, block);
+  }
+  else {
+    THIS->OutputBlocks[InputNo]->EXECUTE_PTR(
+        THIS->OutputBlocks[InputNo], THIS->OutputBlocks_InputNo[InputNo],
+        THIS->State[InputNo][THIS->max_delay - THIS->current_delay], block);
+  }
+
+  // update buffer
+  memcpy(THIS->State[InputNo], THIS->State[InputNo]+1, sizeof(DSP::Float)*(THIS->max_delay-1));
+  THIS->State[InputNo][THIS->max_delay-1]=value;
+};
+
+void DSP::u::AdjustableDelay::InputExecute_with_cyclic_buffer(INPUT_EXECUTE_ARGS)
+{
+  UNUSED_ARGUMENT(InputNo);
+  UNUSED_DEBUG_ARGUMENT(Caller);
+
+  THIS->UpdateDelayIfNeeded();
+
+  if (THIS->current_delay == 0) {
+    THIS->OutputBlocks[0]->EXECUTE_PTR(
+        THIS->OutputBlocks[0], THIS->OutputBlocks_InputNo[0],
+        value, block);
+  }
+  else {
+    // Calculate actual current_delay index
+    // THIS->index[0] points to max_delay
+    // (index[0] + max_delay) % max_delay == index[0] => delay <- max_delay
+    // (index[0] + max_delay -1) % max_delay => delay <- 1 = max_delay - (max_delay - 1)
+    // (index[0] + 1) % max_delay => delay <- max_delay - 1
+    unsigned int current_index = 
+        (THIS->index[0] + (THIS->max_delay - THIS->current_delay)) % THIS->max_delay;
+
+    THIS->OutputBlocks[0]->EXECUTE_PTR(
+        THIS->OutputBlocks[0], THIS->OutputBlocks_InputNo[0],
+        THIS->State[0][THIS->index[current_index]], block);
+  }
+
+  // update buffer
+  THIS->State[0][THIS->index[0]]=value;
+
+  THIS->index[0]++;
+  THIS->index[0] %= THIS->max_delay;
+};
+
+void DSP::u::AdjustableDelay::InputExecute_with_cyclic_buffer_multi(INPUT_EXECUTE_ARGS)
+{
+  UNUSED_DEBUG_ARGUMENT(Caller);
+
+  THIS->UpdateDelayIfNeeded();
+
+  if (THIS->current_delay == 0) {
+    THIS->OutputBlocks[InputNo]->EXECUTE_PTR(
+        THIS->OutputBlocks[InputNo], THIS->OutputBlocks_InputNo[InputNo],
+        value, block);
+  }
+  else {
+    // Calculate actual current_delay index
+    unsigned int current_index = 
+        (THIS->index[InputNo] + (THIS->max_delay - THIS->current_delay)) % THIS->max_delay;
+
+    THIS->OutputBlocks[InputNo]->EXECUTE_PTR(
+        THIS->OutputBlocks[InputNo], THIS->OutputBlocks_InputNo[InputNo],
+        THIS->State[InputNo][current_index], block);
+  }
+
+  // update buffer
+  THIS->State[InputNo][THIS->index[InputNo]]=value;
+
+  THIS->index[InputNo]++;
+  THIS->index[InputNo] %= THIS->max_delay;
+}
+#undef THIS
+
 /**************************************************/
 // addition node
 DSP::u::Addition::Addition(unsigned int NoOfRealInputs_in, unsigned int NoOfComplexInputs_in)
diff --git a/src/cpp/DSP_modules2.cpp b/src/cpp/DSP_modules2.cpp
index 8a37568..2fdb1b9 100644
--- a/src/cpp/DSP_modules2.cpp
+++ b/src/cpp/DSP_modules2.cpp
@@ -1793,8 +1793,8 @@ unsigned int getConstellation(
         unsigned int n = 0;
         for (unsigned int ind=0; ind < M; ind++) {
           n = ind ^ (ind >> 1); // convert to gray coding
-          constellation[ind].re = static_cast<DSP::Float>(cos(constellation_phase_offset+(DSP::M_PIx2*n)/DSP::Float(M)));
-          constellation[ind].im = static_cast<DSP::Float>(sin(constellation_phase_offset+(DSP::M_PIx2*n)/DSP::Float(M)));
+          constellation[ind].re = static_cast<DSP::Float>(cos(constellation_phase_offset+(DSP::M_PIx2*DSP::Float(n))/DSP::Float(M)));
+          constellation[ind].im = static_cast<DSP::Float>(sin(constellation_phase_offset+(DSP::M_PIx2*DSP::Float(n))/DSP::Float(M)));
 
 //          stringstream ss;
 //          ss << "constellation[" << ind << "]={" << std::setprecision(2) << constellation[ind].re << "," << constellation[ind].im << "}; n=" << n;
diff --git a/src/include/DSP_lib.h b/src/include/DSP_lib.h
index 8e95267..572213c 100644
--- a/src/include/DSP_lib.h
+++ b/src/include/DSP_lib.h
@@ -11,7 +11,7 @@
 
 #define DSP_VER_MAJOR 0
 #define DSP_VER_MINOR 20
-#define DSP_VER_BUILD 29 // !!! without zeroes before, else this will be treated as octal number
+#define DSP_VER_BUILD 30 // !!! without zeroes before, else this will be treated as octal number
 #define DSP_VER_YEAR  2023
 #define DSP_VER       DSP_VER_MAJOR.DSP_VER_MINOR.DSP_VER_BUILD
 
@@ -588,8 +588,8 @@ std::string DSP::lib_version_string();
  *  but they have also inputs.
  *
  *  Basic block for separating input form output in feedback loops
- *  is the delay block DSP::u::LoopDelay. Note that there is also
- *  DSP::u::Delay block but it cannot be used in the feedback loop.
+ *  is the delay block DSP::u::LoopDelay. Note that there are also
+ *  DSP::u::Delay and DSP::u::AdjustableDelay blocks but they cannot be used in the feedback loop.
  *  However DSP::u::Delay should be used in all other cases
  *  because it is implemented in more efficient way than
  *  DSP::u::LoopDelay.
@@ -714,13 +714,13 @@ std::string DSP::lib_version_string();
  *   -# DSP::u::ABS calculates absolute value of real or complex sample
  *   -# DSP::u::Accumulator implements standard accumulator or accumulator with leakage
  *   -# DSP::u::Addition addition block (also weighted summation)
+ *   -# DSP::u::AdjustableDelay variant of DSP::u::Delay with programaticaly controlable delay (between 0 and user defined max_delay).
  *   -# DSP::u::Amplifier multiplies input value by given constant
  *   -# DSP::u::Angle calculates the phase of a complex sample
  *   -# DSP::u::CCPC CCPC - Cartesian coordinates to polar coordinates converter
  *   -# DSP::u::CMPO CMPO - complex mutual power operator
  *   -# DSP::u::Conjugation calculates complex conjugation
- *   -# DSP::u::CyclicDelay this block is OBSOLETE use DSP::u::Delay instead.
- *   -# DSP::u::Delay delay element implemented in processing block mode. Inefficient for large buffer (implemented using memcopy). See DSP::u::CyclicDelay.
+ *   -# DSP::u::Delay delay element implemented as processing block. For large delays cyclic buffer variant (IsBufferCyclic = true) is much more efficient.
  *   -# DSP::u::Differator Differator - first order backward difference operator
  *   -# DSP::u::FIR FIR filter implementation
  *   -# DSP::u::IIR IIR filter implementation
diff --git a/src/include/DSP_modules.h b/src/include/DSP_modules.h
index 69aab0c..9bf2483 100644
--- a/src/include/DSP_modules.h
+++ b/src/include/DSP_modules.h
@@ -51,6 +51,7 @@ namespace DSP {
   namespace u {
     class Copy;
     class Delay;
+    class AdjustableDelay;
     class LoopDelay;
     class Splitter;
     class Amplifier;
@@ -2039,6 +2040,62 @@ class DSP::u::Delay : public DSP::Block
     virtual ~Delay(void);
 };
 
+//! Adjustable delay element implemented in processing mode
+/*! \warning Cannot separate processing in digital feedback loop !!!
+ *
+ * Inputs and Outputs names:
+ *   - Output:
+ *    -# "out" (vector of all outputs)
+ *    -# "out1", "out2", ...
+ *    -# "out.re" == "out1"
+ *    -# "out.im" == "out2"
+ *    .
+ *   - Input:
+ *    -# "in" (vector of all inputs)
+ *    -# "in1", "in2", ...
+ *    -# "in.re" == "in1"
+ *    -# "in.im" == "in2"
+ *    .
+ */
+class DSP::u::AdjustableDelay : public DSP::Block
+{
+  private:
+    unsigned int max_delay;
+    unsigned int current_delay;
+    unsigned int new_delay; //
+    DSP::Float **State;
+    //! current index in buffer
+    unsigned int *index;
+
+    //! Updates current_delay at the begining of the clock's cycle as well as NoOfInputsProcessed
+    void UpdateDelayIfNeeded(void);
+
+    //! version for Delay == 0
+    static void InputExecute_D0(INPUT_EXECUTE_ARGS);
+    static void InputExecute_D0_multi(INPUT_EXECUTE_ARGS);
+    //! version for Delay == 1
+    static void InputExecute_D1(INPUT_EXECUTE_ARGS);
+    static void InputExecute_D1_multi(INPUT_EXECUTE_ARGS);
+    //! version with memcpy
+    static void InputExecute(INPUT_EXECUTE_ARGS);
+    static void InputExecute_multi(INPUT_EXECUTE_ARGS);
+    //! version with cyclic buffer
+    static void InputExecute_with_cyclic_buffer(INPUT_EXECUTE_ARGS);
+    static void InputExecute_with_cyclic_buffer_multi(INPUT_EXECUTE_ARGS);
+  public:
+    //! Sets new value of the delay.
+    /*! Returns actually set delay (will differ from new_delay it is is out of range)
+    * delay value will be updated at the begining of new clock cycle 
+    * before that the GetCurrentDelay returns the previous delay value.
+    */
+    unsigned int SetDelay(unsigned int new_delay_in);
+    //! Returns current delay
+    unsigned int GetCurrentDelay(void);
+
+    //! DSP::u::AdjustableDelay block constructor
+    AdjustableDelay(unsigned int max_delay_in = 1, unsigned int initial_delay = 0, unsigned int InputsNo = 1, bool IsBufferCyclic = true);
+    virtual ~AdjustableDelay(void);
+};
 /**************************************************/
 //! Outputs input value to multiple outputs
 /*! Inputs and Outputs names:
-- 
GitLab