From 285795e74be8c7ddf6ee03be6237e69d19a7a5f7 Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Sun, 18 Feb 2024 16:02:24 +0100 Subject: [PATCH 01/25] Added specializations to Line for U/SFix --- Line.h | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) diff --git a/Line.h b/Line.h index f433907d3..d0c632756 100644 --- a/Line.h +++ b/Line.h @@ -18,6 +18,8 @@ #include "WProgram.h" #endif +#include + /** For linear changes with a minimum of calculation at each step. For instance, you can use Line to make an oscillator glide from one frequency to another, pre-calculating the required phase increments for each end and then letting your @@ -33,6 +35,9 @@ represent fractional numbers. Google "fixed point arithmetic" if this is new to you. */ + + + template class Line { @@ -315,6 +320,174 @@ class Line } }; + +/* UFix specialisation */ +template +class Line> +{ +private: + typedef UFix internal_type; + internal_type current_value; + internal_type step_size; + +public: + /** Constructor. Use the template parameter to set the type of numbers you + want to use. For example, Line \ myline; makes a Line which uses ints. + */ + Line (){;} + + /** Increments one step along the line. + @return the next value. + */ + inline + internal_type next() + { + current_value = current_value + step_size; + return current_value; + } + + /** Set the current value of the line. + The Line will continue incrementing from this + value using any previously calculated step size. + @param value the number to set the Line's current_value to. + */ + inline + void set(internal_type value) + { + current_value=value; + } + + /** Given a target value and the number of steps to take on the way, this calculates the step size needed to get there from the current value. + @param targetvalue the value to move towards. + @param num_steps how many steps to take to reach the target as a UFix<_NI,0> + */ + template + void set(internal_type targetvalue, UFix<_NI,0> num_steps) + { + if(num_steps.asRaw()) { + internal_type numerator = targetvalue-current_value; + step_size = numerator*num_steps.invAccurate(); + } else { + step_size = 0; + current_value = targetvalue; + } + } + + + /** Given a target value and the number of steps to take on the way, this calculates the step size needed to get there from the current value. + @param targetvalue the value to move towards. + @param num_steps how many steps to take to reach the target. + */ + template + void set(internal_type targetvalue, T num_steps) + { + if(num_steps) { + internal_type numerator = targetvalue-current_value; + step_size = internal_type(numerator.asRaw()/num_steps,true); + } else { + step_size = 0; + current_value = targetvalue; + } + } + + /** Given a new starting value, target value and the number of steps to take on the way, this sets the step size needed to get there. + @param startvalue the number to set the Line's current_value to. + @param targetvalue the value to move towards. + @param num_steps how many steps to take to reach the target. + */ + template + void set(internal_type startvalue, internal_type targetvalue, T num_steps) + { + set(startvalue); + set(targetvalue, num_steps); + } +}; + + +/* SFix specialisation (if someone has an idea to avoid duplication with UFix) */ +template +class Line> +{ +private: + typedef SFix internal_type; + internal_type current_value; + internal_type step_size; + +public: + /** Constructor. Use the template parameter to set the type of numbers you + want to use. For example, Line \ myline; makes a Line which uses ints. + */ + Line (){;} + + /** Increments one step along the line. + @return the next value. + */ + inline + internal_type next() + { + current_value = current_value + step_size; + return current_value; + } + + /** Set the current value of the line. + The Line will continue incrementing from this + value using any previously calculated step size. + @param value the number to set the Line's current_value to. + */ + inline + void set(internal_type value) + { + current_value=value; + } + + /** Given a target value and the number of steps to take on the way, this calculates the step size needed to get there from the current value. + @param targetvalue the value to move towards. + @param num_steps how many steps to take to reach the target as a UFix<_NI,0> + */ + template + void set(internal_type targetvalue, UFix<_NI,0> num_steps) + { + if(num_steps.asRaw()) { + internal_type numerator = targetvalue-current_value; + step_size = numerator*num_steps.invAccurate(); + } else { + step_size = 0; + current_value = targetvalue; + } + } + + + /** Given a target value and the number of steps to take on the way, this calculates the step size needed to get there from the current value. + @param targetvalue the value to move towards. + @param num_steps how many steps to take to reach the target. + */ + template + void set(internal_type targetvalue, T num_steps) + { + if(num_steps) { + internal_type numerator = targetvalue-current_value; + step_size = internal_type(numerator.asRaw()/num_steps,true); + } else { + step_size = 0; + current_value = targetvalue; + } + } + + /** Given a new starting value, target value and the number of steps to take on the way, this sets the step size needed to get there. + @param startvalue the number to set the Line's current_value to. + @param targetvalue the value to move towards. + @param num_steps how many steps to take to reach the target. + */ + template + void set(internal_type startvalue, internal_type targetvalue, T num_steps) + { + set(startvalue); + set(targetvalue, num_steps); + } +}; + + + /** @example 02.Control/Control_Tremelo/Control_Tremelo.ino This example demonstrates the Line class. From 59b1778ce1fd176a899efc9c3d3697a97abf506d Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Sun, 18 Feb 2024 18:04:10 +0100 Subject: [PATCH 02/25] Added U/SFix specialization to Smooth.h --- Smooth.h | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) diff --git a/Smooth.h b/Smooth.h index c5bc62280..34fab9efe 100644 --- a/Smooth.h +++ b/Smooth.h @@ -268,6 +268,160 @@ class Smooth /** @endcond */ + +/* Specialization for UFix */ +template +class Smooth> +{ +private: + typedef UFix internal_type; + internal_type last_out; + UFix<0,16> a; + +public: + + + /** Constructor. + @param smoothness sets how much smoothing the filter will apply to + its input. Use a float or a UFix<0,NF> in the range 0~1, where 0 is not very smooth and 0.99 is + very smooth. + */ + template + Smooth(T smoothness) + { + setSmoothness(smoothness); + } + + /** Constructor. + This constructor which doesn't take a smoothness parameter is useful when you incorporate Smooth into another class definition. + You need to call setSmoothness(float) for your object before using Smooth. + @note there's probably a better way to do this... + */ + Smooth() + {} + + + /** Filters the input and returns the filtered value. You can also use the operator() function, eg. something like mySmoother(input-value). + @param in the signal to be smoothed. + @return the filtered signal. + */ + inline + internal_type next(internal_type in) + { + internal_type out = last_out + a * (in - last_out); // With FixMath, the syntax is actually the same than with floats :) + last_out = out; + return out; + } + + + + inline + internal_type operator()(internal_type n) { + return next(n); + } + + /** Sets how much smoothing the filter will apply to its input. + @param smoothness sets how much smoothing the filter will apply to + its input. Use a float in the range 0~1, where 0 is not very smooth and 0.99 is + very smooth. + */ + inline + void setSmoothness(float smoothness) + { + a=internal_type(1.f-smoothness); + } + + /** Sets how much smoothing the filter will apply to its input. + @param smoothness sets how much smoothing the filter will apply to + its input. Use a UFix<0,NF> in the range 0~1, where 0 is not very smooth and 0.99 is + very smooth. + */ + template + void setSmoothness(UFix<0,_NF> smoothness) + { + a = UFix<1,0>(1) - smoothness; + } + +}; + + + + +/* Specialization for SFix */ +template +class Smooth> +{ +private: + typedef SFix internal_type; + internal_type last_out; + UFix<0,16> a; + +public: + + + /** Constructor. + @param smoothness sets how much smoothing the filter will apply to + its input. Use a float or a UFix<0,NF> in the range 0~1, where 0 is not very smooth and 0.99 is + very smooth. + */ + template + Smooth(T smoothness) + { + setSmoothness(smoothness); + } + + /** Constructor. + This constructor which doesn't take a smoothness parameter is useful when you incorporate Smooth into another class definition. + You need to call setSmoothness(float) for your object before using Smooth. + @note there's probably a better way to do this... + */ + Smooth() + {} + + + /** Filters the input and returns the filtered value. You can also use the operator() function, eg. something like mySmoother(input-value). + @param in the signal to be smoothed. + @return the filtered signal. + */ + inline + internal_type next(internal_type in) + { + internal_type out = last_out + a * (in - last_out); + last_out = out; + return out; + } + + + + inline + internal_type operator()(internal_type n) { + return next(n); + } + + /** Sets how much smoothing the filter will apply to its input. + @param smoothness sets how much smoothing the filter will apply to + its input. Use a float in the range 0~1, where 0 is not very smooth and 0.99 is + very smooth. + */ + inline + void setSmoothness(float smoothness) + { + a=internal_type(1.f-smoothness); + } + + /** Sets how much smoothing the filter will apply to its input. + @param smoothness sets how much smoothing the filter will apply to + its input. Use a UFix<0,NF> in the range 0~1, where 0 is not very smooth and 0.99 is + very smooth. + */ + template + void setSmoothness(UFix<0,_NF> smoothness) + { + a = UFix<1,0>(1) - smoothness; + } + +}; + /** @example 05.Control_Filters/Smooth/Smooth.ino This example demonstrates the Smooth class. From ebca5ab0ddbf7148482afd2ff1cf8c88d7ef724b Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Mon, 19 Feb 2024 23:25:36 +0100 Subject: [PATCH 03/25] Added an example of FM synthesis with FixMath --- examples/06.Synthesis/FMsynth/FMsynth.ino | 3 + .../FMsynth_FixMath/FMsynth_FixMath.ino | 103 ++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 examples/06.Synthesis/FMsynth_FixMath/FMsynth_FixMath.ino diff --git a/examples/06.Synthesis/FMsynth/FMsynth.ino b/examples/06.Synthesis/FMsynth/FMsynth.ino index b21c40ebf..a411e5ff0 100644 --- a/examples/06.Synthesis/FMsynth/FMsynth.ino +++ b/examples/06.Synthesis/FMsynth/FMsynth.ino @@ -20,6 +20,9 @@ https://groups.google.com/forum/#!forum/mozzi-users Tim Barrass 2012, CC by-nc-sa. + + Note: an similar example, but using the newer FixMath + framework is given is FMsynth_FixMath. */ #include diff --git a/examples/06.Synthesis/FMsynth_FixMath/FMsynth_FixMath.ino b/examples/06.Synthesis/FMsynth_FixMath/FMsynth_FixMath.ino new file mode 100644 index 000000000..552ec2bba --- /dev/null +++ b/examples/06.Synthesis/FMsynth_FixMath/FMsynth_FixMath.ino @@ -0,0 +1,103 @@ +/* Example of simple FM with the phase modulation technique, + using Mozzi sonification library. + + Demonstrates Oscil::phMod() for phase modulation, + Smooth() for smoothing control signals, + and FixMath fixed point number types for fractional frequencies. + This is the same technique than the FMsynth example but + using FixMath instead of mozzi_fixmath. + + Mozzi documentation/API + https://sensorium.github.io/Mozzi/doc/html/index.html + + Mozzi help/discussion/announcements: + https://groups.google.com/forum/#!forum/mozzi-users + + Thomas Combriat and the Mozzi team 2023, CC by-nc-sa. +*/ + + + +#include +#include +#include // table for Oscils to play +#include +#include +#include +#include + + +#define CONTROL_RATE 256 // Hz, powers of 2 are most reliable + +Oscil aCarrier(COS2048_DATA); +Oscil aModulator(COS2048_DATA); +Oscil kModIndex(COS2048_DATA); + +UFix<0, 16> mod_index; +UFix<16, 16> deviation; + +UFix<16, 16> carrier_freq, mod_freq; +const UFix<0,16> modulation_amp = 0.001; // how much the modulation index will vary around its mean +const UFix<2,0> mean_modulation_unscaled = 2; // the real mean modulation will be mean_modulation_unscaled * modulation_max + // this one is adding a bias to the oscillator, hence it should be bigger than one. + +const UFix<2, 0> mod_to_carrier_ratio = 3; // 3 fits in UFix<2,0> which has a maximum range of (2^2)-1=3 + +EventDelay kNoteChangeDelay; + +const UFix<7, 0> note_upper_limit = 50, note_lower_limit = 32; + +UFix<7, 0> note0 = note_lower_limit, note1 = note_lower_limit + UFix<7, 0>(5), target_note = note0; +SFix<2, 0> note_change_step = 3; // will only take +3 or -3, 2bits are enough for that + +UFix<7,8> smoothed_note; + +Smooth> kSmoothNote(0.95f); + +void setup() { + kNoteChangeDelay.set(768); + kModIndex.setFreq(.768f); // sync with kNoteChangeDelay + startMozzi(CONTROL_RATE); +} + +void setFreqs(UFix<7, 8> midi_note) { + carrier_freq = mtof(midi_note); + mod_freq = carrier_freq * mod_to_carrier_ratio; + deviation = mod_freq * mod_index; + aCarrier.setFreq(carrier_freq); + aModulator.setFreq(mod_freq); +} + +void updateControl() { + if (kNoteChangeDelay.ready()) { + if (target_note == note0) + { + note1 = note1 + note_change_step; + target_note = note1; + } + else + { + note0 = note0 + note_change_step; + target_note = note0; + } + if(note0>note_upper_limit) note_change_step = -3; + if(note0 Date: Mon, 19 Feb 2024 23:32:26 +0100 Subject: [PATCH 04/25] Removed volatile in Line.h --- Line.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Line.h b/Line.h index d0c632756..34ae20856 100644 --- a/Line.h +++ b/Line.h @@ -42,8 +42,8 @@ template class Line { private: - volatile T current_value; // volatile because it could be set in control interrupt and updated in audio - volatile T step_size; + T current_value; + T step_size; public: /** Constructor. Use the template parameter to set the type of numbers you @@ -117,7 +117,7 @@ template <> class Line { private: - volatile unsigned char current_value; // volatile because it could be set in control interrupt and updated in audio + unsigned char current_value; char step_size; public: @@ -186,7 +186,7 @@ template <> class Line { private: - volatile unsigned int current_value; // volatile because it could be set in control interrupt and updated in audio + unsigned int current_value; int step_size; public: @@ -258,7 +258,7 @@ template <> class Line { private: - volatile unsigned long current_value; // volatile because it could be set in control interrupt and updated in audio + unsigned long current_value; long step_size; public: From 8d69afca841c5f8cdf740f5a3b6e0f5fb15d43d2 Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Sat, 24 Feb 2024 22:01:42 +0100 Subject: [PATCH 05/25] Made a more specific template in Oscil for FixMath --- Oscil.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Oscil.h b/Oscil.h index 0e55ec454..4921295b8 100644 --- a/Oscil.h +++ b/Oscil.h @@ -214,11 +214,11 @@ class Oscil @note This didn't run faster than float last time it was tested, after 2014 code changes. Need to see if 2014 changes improved or worsened performance. @param frequency in UFix fixed-point number format. */ - template + template inline - void setFreq(UFix frequency) + void setFreq(UFix frequency) { - setFreq_Q16n16(UFix<16,16>(frequency).asRaw()); + setFreq_Q16n16(UFix<16,16>(frequency).asRaw()); } From 5f2f36ea78dd20399f062e7b540c7c00445c712a Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Sat, 24 Feb 2024 22:02:04 +0100 Subject: [PATCH 06/25] Added Difference_Tone adapted to FixMath Added documentation --- .../Difference_Tone/Difference_Tone.ino | 2 + .../Difference_Tone_FixMath.ino | 71 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 examples/06.Synthesis/Difference_Tone_FixMath/Difference_Tone_FixMath.ino diff --git a/examples/06.Synthesis/Difference_Tone/Difference_Tone.ino b/examples/06.Synthesis/Difference_Tone/Difference_Tone.ino index 46d2efbc4..9e4028ecd 100644 --- a/examples/06.Synthesis/Difference_Tone/Difference_Tone.ino +++ b/examples/06.Synthesis/Difference_Tone/Difference_Tone.ino @@ -3,6 +3,8 @@ using Mozzi sonification library. Demonstrates the use of EventDelay(), rand() and fixed-point numbers. + + Note that an example using the newer FixMath paradigm is also available: Difference_Tone_FixMath. Circuit: Audio output on digital pin 9 on a Uno or similar, or DAC/A14 on Teensy 3.1, or diff --git a/examples/06.Synthesis/Difference_Tone_FixMath/Difference_Tone_FixMath.ino b/examples/06.Synthesis/Difference_Tone_FixMath/Difference_Tone_FixMath.ino new file mode 100644 index 000000000..f9648964c --- /dev/null +++ b/examples/06.Synthesis/Difference_Tone_FixMath/Difference_Tone_FixMath.ino @@ -0,0 +1,71 @@ +/* Example using clipping to modify the spectrum of an audio signal + and emphasise a tone generated by the difference in frequency of 2 waves, + using Mozzi sonification library. + + Adaptation of the Difference_Tone example using FixMath. + + Demonstrates the use of EventDelay(), rand() and fixed-point numbers. + + Demonstrates Oscil::phMod() for phase modulation, + Smooth() for smoothing control signals, + and FixMath fixed point number types for fractional frequencies. + This is the same technique than the FMsynth example but + using FixMath instead of mozzi_fixmath. + + Mozzi documentation/API + https://sensorium.github.io/Mozzi/doc/html/index.html + + Mozzi help/discussion/announcements: + https://groups.google.com/forum/#!forum/mozzi-users + + Tim Barras, Thomas Combriat and the Mozzi team 2023, CC by-nc-sa. +*/ + + +#include +#include +#include +#include +#include +#include +#include + + +// use: Oscil oscilName (wavetable), look in .h file of table #included above +Oscil aSin1(SIN2048_DATA); // sine wave sound source +Oscil aSin2(SIN2048_DATA); // sine wave sound source +Oscil aGain(SIN2048_DATA); // to fade audio signal in and out before waveshaping + +// for scheduling note changes +EventDelay kChangeNoteDelay; + +const UFix<8, 0> freq1 = 184; +const auto harmonic_step = freq1 * UFix<8, 0>(12).invAccurate(); // harmonic_step = freq1/12; + +void setup() { + Serial.begin(115200); + aSin1.setFreq(freq1); + aGain.setFreq(0.2f); // use a float for low frequencies, in setup it doesn't need to be fast + kChangeNoteDelay.set(2000); // note duration ms, within resolution of CONTROL_RATE + startMozzi(); // :) +} + +void updateControl() { + if (kChangeNoteDelay.ready()) { + UFix<4, 0> harmonic = rand((byte)12); + auto shimmer = toUFraction(rand((byte)255)); // Creates a UFix<0,8> + auto freq2difference = (harmonic * harmonic_step) + (harmonic_step * shimmer).sR<3>(); // the shimmering is divided by 8 here + auto freq2 = (freq1 - freq2difference).asUFix(); + aSin2.setFreq((freq2)); + kChangeNoteDelay.start(); + } +} + +AudioOutput_t updateAudio() { + auto asig = (toSInt(aSin1.next()) + toSInt(aSin2.next())) * (toSFraction(aGain.next()) + UFix<1, 7>(1.2)); // this is a SFix<9,9> in the end + return MonoOutput::fromAlmostNBit(11, asig.asRaw()).clip(); // TODO, implement smart MonoOutput +} + +void loop() { + audioHook(); // required here +} From b0886664b69136b100824d3f1c275627dce16f04 Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Sun, 25 Feb 2024 15:03:09 +0100 Subject: [PATCH 07/25] Adapted Detuned_Beats_Wash example --- .../Detuned_Beats_Wash/Detuned_Beats_Wash.ino | 80 +++++++++++-------- 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/examples/06.Synthesis/Detuned_Beats_Wash/Detuned_Beats_Wash.ino b/examples/06.Synthesis/Detuned_Beats_Wash/Detuned_Beats_Wash.ino index 05eb161d1..86d772a14 100644 --- a/examples/06.Synthesis/Detuned_Beats_Wash/Detuned_Beats_Wash.ino +++ b/examples/06.Synthesis/Detuned_Beats_Wash/Detuned_Beats_Wash.ino @@ -6,12 +6,12 @@ updateControl(), and the outputs of 12 audio oscillators are summed in updateAudio(). - Demonstrates the use of fixed-point Q16n16 + Demonstrates the use of FixMath fixed point format numbers, mtof() for converting midi note values to frequency, and xorshift96() for random numbers. This sketch is pushing the limits of computing power on the - 8-biit AVR boards. At the time of this writing, you will have + 8-bit AVR boards. At the time of this writing, you will have to manually alter your platform.txt file to use optimization for speed rather than size on Arduino Uno and similar. (Alternatively, remove one of the oscillators) @@ -26,7 +26,7 @@ Mozzi help/discussion/announcements: https://groups.google.com/forum/#!forum/mozzi-users - Tim Barrass 2012, CC by-nc-sa. + Tim Barrass 2012, Thomas Combriat and the Mozzi team 2024, CC by-nc-sa. */ #include @@ -34,7 +34,7 @@ #include #include #include -#include // for Q16n16 fixed-point fractional number type +#include // harmonics Oscil aCos1(COS8192_DATA); @@ -55,13 +55,19 @@ Oscil aCos6b(COS8192_DATA); //Oscil aCos7b(COS8192_DATA); // base pitch frequencies in Q16n16 fixed int format (for speed later) -Q16n16 f1,f2,f3,f4,f5,f6;//,f7; +UFix<12,15> f1,f2,f3,f4,f5,f6;//,f7; -Q16n16 variation() { +/*Q16n16 variation() { // 32 random bits & with 524287 (b111 1111 1111 1111 1111) // gives between 0-8 in Q16n16 format return (Q16n16) (xorshift96() & 524287UL); +}*/ + +UFix<3,16> variation() // changing the return type here enables to easily + // increase or decrease the variation +{ + return UFix<3,16>::fromRaw(xorshift96() & 524287UL); } @@ -69,31 +75,31 @@ void setup(){ startMozzi(); // select base frequencies using mtof (midi to freq) and fixed-point numbers - f1 = Q16n16_mtof(Q16n0_to_Q16n16(48)); - f2 = Q16n16_mtof(Q16n0_to_Q16n16(74)); - f3 = Q16n16_mtof(Q16n0_to_Q16n16(64)); - f4 = Q16n16_mtof(Q16n0_to_Q16n16(77)); - f5 = Q16n16_mtof(Q16n0_to_Q16n16(67)); - f6 = Q16n16_mtof(Q16n0_to_Q16n16(57)); - // f7 = Q16n16_mtof(Q16n0_to_Q16n16(60)); + f1 = mtof(UFix<7,0>(48)); + f2 = mtof(UFix<7,0>(74)); + f3 = mtof(UFix<7,0>(64)); + f4 = mtof(UFix<7,0>(77)); + f5 = mtof(UFix<7,0>(67)); + f6 = mtof(UFix<7,0>(57)); + // f7 = mtof(UFix<7,0>(60)); // set Oscils with chosen frequencies - aCos1.setFreq_Q16n16(f1); - aCos2.setFreq_Q16n16(f2); - aCos3.setFreq_Q16n16(f3); - aCos4.setFreq_Q16n16(f4); - aCos5.setFreq_Q16n16(f5); - aCos6.setFreq_Q16n16(f6); - // aCos7.setFreq_Q16n16(f7); + aCos1.setFreq(f1); + aCos2.setFreq(f2); + aCos3.setFreq(f3); + aCos4.setFreq(f4); + aCos5.setFreq(f5); + aCos6.setFreq(f6); + // aCos7.setFreq(f7); // set frequencies of duplicate oscillators - aCos1b.setFreq_Q16n16(f1+variation()); - aCos2b.setFreq_Q16n16(f2+variation()); - aCos3b.setFreq_Q16n16(f3+variation()); - aCos4b.setFreq_Q16n16(f4+variation()); - aCos5b.setFreq_Q16n16(f5+variation()); - aCos6b.setFreq_Q16n16(f6+variation()); - //aCos7b.setFreq_Q16n16(f7+variation()); + aCos1b.setFreq(f1+variation()); + aCos2b.setFreq(f2+variation()); + aCos3b.setFreq(f3+variation()); + aCos4b.setFreq(f4+variation()); + aCos5b.setFreq(f5+variation()); + aCos6b.setFreq(f6+variation()); + //aCos7b.setFreq(f7+variation()); } @@ -110,31 +116,31 @@ void updateControl(){ switch (lowByte(xorshift96()) & 7){ // 7 is 0111 case 0: - aCos1b.setFreq_Q16n16(f1+variation()); + aCos1b.setFreq(f1+variation()); break; case 1: - aCos2b.setFreq_Q16n16(f2+variation()); + aCos2b.setFreq(f2+variation()); break; case 2: - aCos3b.setFreq_Q16n16(f3+variation()); + aCos3b.setFreq(f3+variation()); break; case 3: - aCos4b.setFreq_Q16n16(f4+variation()); + aCos4b.setFreq(f4+variation()); break; case 4: - aCos5b.setFreq_Q16n16(f5+variation()); + aCos5b.setFreq(f5+variation()); break; case 5: - aCos6b.setFreq_Q16n16(f6+variation()); + aCos6b.setFreq(f6+variation()); break; /* case 6: - aCos7b.setFreq_Q16n16(f7+variation()); + aCos7b.setFreq(f7+variation()); break; */ } @@ -151,6 +157,10 @@ AudioOutput_t updateAudio(){ aCos5.next() + aCos5b.next() + aCos6.next() + aCos6b.next();// + // aCos7.next() + aCos7b.next(); +/* +auto asig = +toSFraction(aCos1.next()) + toSFraction(aCos1b.next()); - return MonoOutput::fromAlmostNBit(12, asig); + return MonoOutput::fromNBit(asig.getNF()+asig.getNI(), asig.asRaw());*/ + return MonoOutput::fromAlmostNBit(12, asig); } From 05ef0f19e40ec6e0f8cca66e7f417e9e41990b45 Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Mon, 26 Feb 2024 20:46:24 +0100 Subject: [PATCH 08/25] Added outputs from SFix for MonoOutput and StereoOutput BugFix --- AudioOutput.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/AudioOutput.h b/AudioOutput.h index 252a3076c..b4e042083 100644 --- a/AudioOutput.h +++ b/AudioOutput.h @@ -46,6 +46,7 @@ #ifndef AUDIOOUTPUT_H #define AUDIOOUTPUT_H +#include /** The type used to store a single channel of a single frame, internally. For compatibility with earlier versions of Mozzi this is defined as int. * If you do not care about keeping old sketches working, you may be able to save some RAM by using int16_t, instead (on boards where int is larger @@ -123,8 +124,11 @@ struct MonoOutput { /** Construct an audio frame from a zero-centered value known to be in the 8 bit range. On AVR, if MOZZI_OUTPUT_PWM mode, this is effectively the same as calling the * constructor, directly (no scaling gets applied). On platforms/configs using more bits, an appropriate left-shift will be performed. */ static inline MonoOutput from8Bit(int16_t l) { return fromNBit(8, l); } - /** Construct an audio frame a zero-centered value known to be in the 16 bit range. This is jsut a shortcut for fromNBit(16, ...) provided for convenience. */ + /** Construct an audio frame from a zero-centered value known to be in the 16 bit range. This is jsut a shortcut for fromNBit(16, ...) provided for convenience. */ static inline MonoOutput from16Bit(int16_t l) { return fromNBit(16, l); } + /** Construct an audio frame from a SFix type from FixMath. Mozzi will figure out how many bits are in there and performs appropriate shifting to match the output range. */ + template + static inline MonoOutput fromSFix(SFix l) { return MonoOutput(SCALE_AUDIO(l.asRaw(), (NI+NF))) ;} /** Construct an audio frame a zero-centered value known to be above at almost but not quite the N bit range, e.g. at N=8 bits and a litte. On most platforms, this is * exactly the same as fromNBit(), shifting up or down to the platforms' available resolution. * @@ -168,6 +172,9 @@ template static inline StereoOutput fromNBit(uint8_t bits, T l, T r) static inline StereoOutput from8Bit(int16_t l, int16_t r) { return fromNBit(8, l, r); } /** See @ref MonoOutput::from16Bit(), stereo variant */ static inline StereoOutput from16Bit(int16_t l, int16_t r) { return fromNBit(16, l, r); } +/** See @ref MonoOutput::fromSFix(), stereo variant. Note that the two channels do not need to have the same number of bits. */ + template + static inline StereoOutput fromSFix(SFix l, SFix<_NI,_NF,_RANGE> r) { return MonoOutput(SCALE_AUDIO(l.asRaw(), (NI+NF)), SCALE_AUDIO(r.asRaw(), (_NI+_NF))); } /** See @ref MonoOutput::fromAlmostNBit(), stereo variant */ template static inline StereoOutput fromAlmostNBit(A bits, B l, B r) { return StereoOutput(SCALE_AUDIO_NEAR(l, bits), SCALE_AUDIO_NEAR(r, bits)); } private: From dbed60aa55f798bec3ef4f3838ce37949a686181 Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Mon, 26 Feb 2024 20:54:45 +0100 Subject: [PATCH 09/25] Added automatic scaling to DBW example Typo in AudioOutput --- AudioOutput.h | 2 +- .../Detuned_Beats_Wash/Detuned_Beats_Wash.ino | 61 +++++++++++++------ 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/AudioOutput.h b/AudioOutput.h index b4e042083..0d655aee3 100644 --- a/AudioOutput.h +++ b/AudioOutput.h @@ -174,7 +174,7 @@ template static inline StereoOutput fromNBit(uint8_t bits, T l, T r) static inline StereoOutput from16Bit(int16_t l, int16_t r) { return fromNBit(16, l, r); } /** See @ref MonoOutput::fromSFix(), stereo variant. Note that the two channels do not need to have the same number of bits. */ template - static inline StereoOutput fromSFix(SFix l, SFix<_NI,_NF,_RANGE> r) { return MonoOutput(SCALE_AUDIO(l.asRaw(), (NI+NF)), SCALE_AUDIO(r.asRaw(), (_NI+_NF))); } + static inline StereoOutput fromSFix(SFix l, SFix<_NI,_NF,_RANGE> r) { return StereoOutput(SCALE_AUDIO(l.asRaw(), (NI+NF)), SCALE_AUDIO(r.asRaw(), (_NI+_NF))); } /** See @ref MonoOutput::fromAlmostNBit(), stereo variant */ template static inline StereoOutput fromAlmostNBit(A bits, B l, B r) { return StereoOutput(SCALE_AUDIO_NEAR(l, bits), SCALE_AUDIO_NEAR(r, bits)); } private: diff --git a/examples/06.Synthesis/Detuned_Beats_Wash/Detuned_Beats_Wash.ino b/examples/06.Synthesis/Detuned_Beats_Wash/Detuned_Beats_Wash.ino index e2bc9038b..7960c5971 100644 --- a/examples/06.Synthesis/Detuned_Beats_Wash/Detuned_Beats_Wash.ino +++ b/examples/06.Synthesis/Detuned_Beats_Wash/Detuned_Beats_Wash.ino @@ -37,22 +37,22 @@ #include // harmonics -Oscil aCos1(COS8192_DATA); -Oscil aCos2(COS8192_DATA); -Oscil aCos3(COS8192_DATA); -Oscil aCos4(COS8192_DATA); -Oscil aCos5(COS8192_DATA); -Oscil aCos6(COS8192_DATA); -//Oscil aCos7(COS8192_DATA); // used to work smoothly in Arduino 1.05 +Oscil aCos1(COS8192_DATA); +Oscil aCos2(COS8192_DATA); +Oscil aCos3(COS8192_DATA); +Oscil aCos4(COS8192_DATA); +Oscil aCos5(COS8192_DATA); +Oscil aCos6(COS8192_DATA); +//Oscil aCos7(COS8192_DATA); // used to work smoothly in Arduino 1.05 // duplicates but slightly off frequency for adding to originals -Oscil aCos1b(COS8192_DATA); -Oscil aCos2b(COS8192_DATA); -Oscil aCos3b(COS8192_DATA); -Oscil aCos4b(COS8192_DATA); -Oscil aCos5b(COS8192_DATA); -Oscil aCos6b(COS8192_DATA); -//Oscil aCos7b(COS8192_DATA); +Oscil aCos1b(COS8192_DATA); +Oscil aCos2b(COS8192_DATA); +Oscil aCos3b(COS8192_DATA); +Oscil aCos4b(COS8192_DATA); +Oscil aCos5b(COS8192_DATA); +Oscil aCos6b(COS8192_DATA); +//Oscil aCos7b(COS8192_DATA); // base pitch frequencies in Q16n16 fixed int format (for speed later) UFix<12,15> f1,f2,f3,f4,f5,f6;//,f7; @@ -148,8 +148,13 @@ void updateControl(){ AudioOutput updateAudio(){ + /* +The following block is the "classical" way of outputting the sound, from a standard C/C++ type. +You need to know how many bits you are dealing with and can use a reduced number to bring in some +distorsion with .clip() if you want. + */ - int asig = + /* int asig = aCos1.next() + aCos1b.next() + aCos2.next() + aCos2b.next() + aCos3.next() + aCos3b.next() + @@ -157,10 +162,26 @@ AudioOutput updateAudio(){ aCos5.next() + aCos5b.next() + aCos6.next() + aCos6b.next();// + // aCos7.next() + aCos7b.next(); -/* -auto asig = -toSFraction(aCos1.next()) + toSFraction(aCos1b.next()); - - return MonoOutput::fromNBit(asig.getNF()+asig.getNI(), asig.asRaw());*/ return MonoOutput::fromAlmostNBit(12, asig); +*/ + + +/* + This is letting Mozzi compute the number of bits for you. + The syntax is a bit more cumbersome but FixMath will be + clever enough to figure out the exact number of bits needed + to create asig without any overflow, but no more. + This number of bits will be used by Mozzi for right/left shifting + the number to match the capability of the system. +*/ + auto asig = + toSFraction(aCos1.next()) + toSFraction(aCos1b.next()) + + toSFraction(aCos2.next()) + toSFraction(aCos2b.next()) + + toSFraction(aCos3.next()) + toSFraction(aCos3b.next()) + + toSFraction(aCos4.next()) + toSFraction(aCos4b.next()) + + toSFraction(aCos5.next()) + toSFraction(aCos5b.next()) + + toSFraction(aCos6.next()) + toSFraction(aCos6b.next()); /* + +toSFraction(aCos7.next()) + toSFraction(aCos7b.next()) +*/ + return MonoOutput::fromSFix(asig); + } From 672ce218ddda5cd72a656b6bc4a1d22db16e0467 Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Mon, 26 Feb 2024 21:32:08 +0100 Subject: [PATCH 10/25] More specific templates for Oscil and mozzi_midi --- Oscil.h | 14 ++++++++------ mozzi_midi.h | 29 +++++++++++++++++++++-------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/Oscil.h b/Oscil.h index deeca59ee..636a4adc8 100644 --- a/Oscil.h +++ b/Oscil.h @@ -157,9 +157,9 @@ class Oscil each direction. This fixed point math number is interpreted as a SFix<15,16> internally. @return a sample from the table. */ - template + template inline - int8_t phMod(SFix phmod_proportion) + int8_t phMod(SFix phmod_proportion) { return phMod(SFix<15,16>(phmod_proportion).asRaw()); } @@ -254,8 +254,9 @@ class Oscil less than 64 Hz. @param frequency in UFix<24,8> fixed-point number format. */ + template inline - void setFreq(UFix<24,8> frequency) + void setFreq(UFix<24,8,RANGE> frequency) { setFreq_Q24n8(frequency.asRaw()); } @@ -293,8 +294,9 @@ class Oscil @note This didn't run faster than float last time it was tested, after 2014 code changes. Need to see if 2014 changes improved or worsened performance. @param frequency in UFix<16,16> fixed-point number format. */ + template inline - void setFreq(UFix<16,16> frequency) + void setFreq(UFix<16,16,RANGE> frequency) { setFreq_Q16n16(frequency.asRaw()); } @@ -307,9 +309,9 @@ class Oscil @note This didn't run faster than float last time it was tested, after 2014 code changes. Need to see if 2014 changes improved or worsened performance. @param frequency in SFix<16,16> fixed-point number format. */ - template + template inline - void setFreq(SFix frequency) + void setFreq(SFix frequency) { setFreq_Q16n16(UFix<16,16>(frequency).asRaw()); } diff --git a/mozzi_midi.h b/mozzi_midi.h index 0f51fca38..94dd04e2b 100644 --- a/mozzi_midi.h +++ b/mozzi_midi.h @@ -113,21 +113,34 @@ inline Q16n16 Q16n16_mtof(Q16n16 midival_fractional) return (Q16n16) (freq1+ diff_fraction); }; -inline UFix<16,16> mtof(UFix<16,16> midival) +/** @ingroup midi +Converts midi note number with speed and accuracy from a UFix<16,16>. +Uses Q16n16_mtof internally. +*/ +template +inline UFix<16,16> mtof(UFix<16,16,RANGE> midival) { return UFix<16,16>::fromRaw(Q16n16_mtof(midival.asRaw())); -} +}; -template - inline UFix<16,16> mtof(UFix midival) +/** @ingroup midi +Converts midi note number with speed and accuracy from any UFix. +Uses Q16n16_mtof internally. +*/ +template +inline UFix<16,16> mtof(UFix midival) { return UFix<16,16>::fromRaw(Q16n16_mtof(UFix<16,16>(midival).asRaw())); -} +}; -template - inline UFix<16,16> mtof(SFix midival) +/** @ingroup midi +Converts midi note number with speed and accuracy from any SFix. +Uses Q16n16_mtof internally. +*/ +template +inline UFix<16,16> mtof(SFix midival) { return UFix<16,16>::fromRaw(Q16n16_mtof(UFix<16,16>(midival).asRaw())); -} +}; #endif /* MOZZI_MIDI_H_ */ From 4b483ddf81df24c48eb523615a330d07da1542b9 Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Mon, 26 Feb 2024 22:01:18 +0100 Subject: [PATCH 11/25] SFix actual number of bits is one extra compared to said size (which is the range of attainable values) --- AudioOutput.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AudioOutput.h b/AudioOutput.h index 0d655aee3..b2d14a0b6 100644 --- a/AudioOutput.h +++ b/AudioOutput.h @@ -128,7 +128,7 @@ struct MonoOutput { static inline MonoOutput from16Bit(int16_t l) { return fromNBit(16, l); } /** Construct an audio frame from a SFix type from FixMath. Mozzi will figure out how many bits are in there and performs appropriate shifting to match the output range. */ template - static inline MonoOutput fromSFix(SFix l) { return MonoOutput(SCALE_AUDIO(l.asRaw(), (NI+NF))) ;} + static inline MonoOutput fromSFix(SFix l) { return MonoOutput(SCALE_AUDIO(l.asRaw(), (NI+NF+1))) ;} /** Construct an audio frame a zero-centered value known to be above at almost but not quite the N bit range, e.g. at N=8 bits and a litte. On most platforms, this is * exactly the same as fromNBit(), shifting up or down to the platforms' available resolution. * @@ -174,7 +174,7 @@ template static inline StereoOutput fromNBit(uint8_t bits, T l, T r) static inline StereoOutput from16Bit(int16_t l, int16_t r) { return fromNBit(16, l, r); } /** See @ref MonoOutput::fromSFix(), stereo variant. Note that the two channels do not need to have the same number of bits. */ template - static inline StereoOutput fromSFix(SFix l, SFix<_NI,_NF,_RANGE> r) { return StereoOutput(SCALE_AUDIO(l.asRaw(), (NI+NF)), SCALE_AUDIO(r.asRaw(), (_NI+_NF))); } + static inline StereoOutput fromSFix(SFix l, SFix<_NI,_NF,_RANGE> r) { return StereoOutput(SCALE_AUDIO(l.asRaw(), (NI+NF+1)), SCALE_AUDIO(r.asRaw(), (_NI+_NF+1))); } /** See @ref MonoOutput::fromAlmostNBit(), stereo variant */ template static inline StereoOutput fromAlmostNBit(A bits, B l, B r) { return StereoOutput(SCALE_AUDIO_NEAR(l, bits), SCALE_AUDIO_NEAR(r, bits)); } private: From a52b44eef91062cfe3bf11285027c0703a2d5200 Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Wed, 28 Feb 2024 21:25:16 +0100 Subject: [PATCH 12/25] Adapted AMSynth example to FixMath --- examples/06.Synthesis/AMsynth/AMsynth.ino | 83 +++++++++++------------ 1 file changed, 40 insertions(+), 43 deletions(-) diff --git a/examples/06.Synthesis/AMsynth/AMsynth.ino b/examples/06.Synthesis/AMsynth/AMsynth.ino index b98892600..ebd03e3e3 100644 --- a/examples/06.Synthesis/AMsynth/AMsynth.ino +++ b/examples/06.Synthesis/AMsynth/AMsynth.ino @@ -11,23 +11,25 @@ DAC/A14 on Teensy 3.1, or check the README or http://sensorium.github.io/Mozzi/ - Mozzi documentation/API - https://sensorium.github.io/Mozzi/doc/html/index.html + Mozzi documentation/API + https://sensorium.github.io/Mozzi/doc/html/index.html - Mozzi help/discussion/announcements: + Mozzi help/discussion/announcements: https://groups.google.com/forum/#!forum/mozzi-users - Tim Barrass 2012, CC by-nc-sa. + Tim Barrass 2012, Thomas Combriat and the Mozzi team 2024, CC by-nc-sa. */ -#define MOZZI_CONTROL_RATE 64 // Hz, powers of 2 are most reliable + +#define MOZZI_CONTROL_RATE 64 // Hz, powers of 2 are most reliable #include #include -#include // table for Oscils to play -#include +#include // table for Oscils to play #include #include #include +#include + // audio oscils Oscil aCarrier(COS2048_DATA); @@ -35,76 +37,71 @@ Oscil aModulator(COS2048_DATA); Oscil aModDepth(COS2048_DATA); // for scheduling note changes in updateControl() -EventDelay kNoteChangeDelay; +EventDelay kNoteChangeDelay; -// synthesis parameters in fixed point formats -Q8n8 ratio; // unsigned int with 8 integer bits and 8 fractional bits -Q24n8 carrier_freq; // unsigned long with 24 integer bits and 8 fractional bits -Q24n8 mod_freq; // unsigned long with 24 integer bits and 8 fractional bits +UFix<8, 8> ratio; // unsigned int with 8 integer bits and 8 fractional bits +UFix<24, 8> carrier_freq; // unsigned long with 24 integer bits and 8 fractional bits // for random notes -Q8n0 octave_start_note = 42; +const UFix<7, 0> octave_start_note = 42; -void setup(){ - ratio = float_to_Q8n8(3.0f); // define modulation ratio in float and convert to fixed-point - kNoteChangeDelay.set(200); // note duration ms, within resolution of MOZZI_CONTROL_RATE - aModDepth.setFreq(13.f); // vary mod depth to highlight am effects - randSeed(); // reseed the random generator for different results each time the sketch runs +void setup() { + ratio = 3; + kNoteChangeDelay.set(200); // note duration ms, within resolution of MOZZI_CONTROL_RATE + aModDepth.setFreq(13.f); // vary mod depth to highlight am effects + randSeed(); // reseed the random generator for different results each time the sketch runs startMozzi(); } -void updateControl(){ - static Q16n16 last_note = octave_start_note; - if(kNoteChangeDelay.ready()){ +void updateControl() { + static auto last_note = octave_start_note; + + if (kNoteChangeDelay.ready()) { // change octave now and then - if(rand((byte)5)==0){ - last_note = 36+(rand((byte)6)*12); + if (rand((byte)5) == 0) { + last_note = UFix<7, 0>(36 + (rand((byte)6) * 12)); } // change step up or down a semitone occasionally - if(rand((byte)13)==0){ - last_note += 1-rand((byte)3); + if (rand((byte)13) == 0) { + last_note = last_note + SFix<7, 0>(1 - rand((byte)3)); } // change modulation ratio now and then - if(rand((byte)5)==0){ - ratio = ((Q8n8) 1+ rand((byte)5)) <<8; + if (rand((byte)5) == 0) { + ratio = 1 + rand((byte)5); } // sometimes add a fractionto the ratio - if(rand((byte)5)==0){ - ratio += rand((byte)255); + if (rand((byte)5) == 0) { + ratio = ratio + toUFraction(rand((byte)255)); } // step up or down 3 semitones (or 0) - last_note += 3 * (1-rand((byte)3)); + last_note = last_note + SFix<7, 0>(3 * (1 - rand((byte)3))); // convert midi to frequency - Q16n16 midi_note = Q8n0_to_Q16n16(last_note); - carrier_freq = Q16n16_to_Q24n8(Q16n16_mtof(midi_note)); + carrier_freq = mtof(last_note); // calculate modulation frequency to stay in ratio with carrier - mod_freq = (carrier_freq * ratio)>>8; // (Q24n8 Q8n8) >> 8 = Q24n8 + auto mod_freq = carrier_freq * ratio; - // set frequencies of the oscillators - aCarrier.setFreq_Q24n8(carrier_freq); - aModulator.setFreq_Q24n8(mod_freq); + // set frequencies of the oscillators + aCarrier.setFreq(carrier_freq); + aModulator.setFreq(mod_freq); // reset the note scheduler kNoteChangeDelay.start(); } - - } -AudioOutput updateAudio(){ - long mod = (128u+ aModulator.next()) * ((byte)128+ aModDepth.next()); - return MonoOutput::fromNBit(24, mod * aCarrier.next()); +AudioOutput updateAudio() { + auto mod = UFix<8, 0>(128 + aModulator.next()) * UFix<8, 0>(128 + aModDepth.next()); + return MonoOutput::fromSFix(mod * toSFraction(aCarrier.next())); } - -void loop(){ +void loop() { audioHook(); } From 1730643cfec7eaaf881ddce43fc2368d9afe36c1 Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Wed, 28 Feb 2024 22:12:19 +0100 Subject: [PATCH 13/25] Adapted Waveshapper_Difference_Tone with FixMath --- .../Waveshaper_Difference_Tone.ino | 86 +++++++++++++++++++ .../Waveshaper_Difference_Tone_FixMath.ino | 79 +++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 examples/06.Synthesis/Waveshaper_Difference_Tone/Waveshaper_Difference_Tone.ino create mode 100644 examples/06.Synthesis/Waveshaper_Difference_Tone_FixMath/Waveshaper_Difference_Tone_FixMath.ino diff --git a/examples/06.Synthesis/Waveshaper_Difference_Tone/Waveshaper_Difference_Tone.ino b/examples/06.Synthesis/Waveshaper_Difference_Tone/Waveshaper_Difference_Tone.ino new file mode 100644 index 000000000..95395e85d --- /dev/null +++ b/examples/06.Synthesis/Waveshaper_Difference_Tone/Waveshaper_Difference_Tone.ino @@ -0,0 +1,86 @@ +/* Example using waveshaping to modify the spectrum of an audio signal + using Mozzi sonification library. + + Demonstrates the use of WaveShaper(), EventDelay(), Smooth(), + rand(), and fixed-point numbers. + + Note that a similar example but using the newer FixMath + library is also available in Waveshaper_Difference_Tone_FixMath. + + Circuit: Audio output on digital pin 9 on a Uno or similar, or + DAC/A14 on Teensy 3.1, or + check the README or http://sensorium.github.com/Mozzi/ + + Mozzi help/discussion/announcements: + https://groups.google.com/forum/#!forum/mozzi-users + + Tim Barrass 2012, CC by-nc-sa. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + + +// use #define for CONTROL_RATE, not a constant +#define CONTROL_RATE 64 // powers of 2 please + +// use: Oscil oscilName (wavetable), look in .h file of table #included above +Oscil aSin1(SIN2048_DATA); // sine wave sound source +Oscil aSin2(SIN2048_DATA); // sine wave sound source +Oscil aGain(SIN2048_DATA); // to fade audio signal in and out before waveshaping + +// for scheduling note changes +EventDelay kChangeNoteDelay; + +// audio frequency as Q16n16 fractional number +Q16n16 freq1 = Q8n0_to_Q16n16(300); + + + + +void setup(){ + startMozzi(CONTROL_RATE); // set a control rate of 64 (powers of 2 please) + aSin1.setFreq_Q16n16(freq1); // set the frequency with a Q16n16 fractional number + aGain.setFreq(0.2f); // use a float for low frequencies, in setup it doesn't need to be fast + kChangeNoteDelay.set(2000); // note duration ms, within resolution of CONTROL_RATE +} + + +void updateControl(){ + if(kChangeNoteDelay.ready()){ + // change proportional frequency of second tone + byte harmonic = rand((byte)12); + byte shimmer = rand((byte)255); + Q16n16 harmonic_step = freq1/12; + Q16n16 freq2difference = harmonic*harmonic_step; + freq2difference += (harmonic_step*shimmer)>>11; + Q16n16 freq2 = freq1-freq2difference; + aSin2.setFreq_Q16n16(freq2); // set the frequency with a Q16n16 fractional number + kChangeNoteDelay.start(); + } +} + + +int updateAudio(){ + int asig = (int)((((uint32_t)aSin1.next()+ aSin2.next())*(200u+aGain.next()))>>3); + int clipped = constrain(asig,-244,243); + return clipped; +} + + +void loop(){ + audioHook(); // required here +} + + + + + + + diff --git a/examples/06.Synthesis/Waveshaper_Difference_Tone_FixMath/Waveshaper_Difference_Tone_FixMath.ino b/examples/06.Synthesis/Waveshaper_Difference_Tone_FixMath/Waveshaper_Difference_Tone_FixMath.ino new file mode 100644 index 000000000..9144846cf --- /dev/null +++ b/examples/06.Synthesis/Waveshaper_Difference_Tone_FixMath/Waveshaper_Difference_Tone_FixMath.ino @@ -0,0 +1,79 @@ +/* Example using waveshaping to modify the spectrum of an audio signal + using Mozzi sonification library. + + Nearly verbatim translation from Waveshaper_Difference_Tone example + but using using FixMath instead of mozzi_fixmath. + + Demonstrates the use of WaveShaper(), EventDelay(), Smooth(), + rand(), and fixed-point numbers using FixMath. + + Circuit: Audio output on digital pin 9 on a Uno or similar, or + DAC/A14 on Teensy 3.1, or + check the README or http://sensorium.github.com/Mozzi/ + + Mozzi help/discussion/announcements: + https://groups.google.com/forum/#!forum/mozzi-users + + Tim Barrass 2012, Thomas Combriat and the Mozzi team 2024, CC by-nc-sa. +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +// use #define for CONTROL_RATE, not a constant +#define MOZZI_CONTROL_RATE 64 // powers of 2 please + +// use: Oscil oscilName (wavetable), look in .h file of table #included above +Oscil aSin1(SIN2048_DATA); // sine wave sound source +Oscil aSin2(SIN2048_DATA); // sine wave sound source +Oscil aGain(SIN2048_DATA); // to fade audio signal in and out before waveshaping + +// for scheduling note changes +EventDelay kChangeNoteDelay; + +const UFix<6, 0> freq1 = 44; // original example says 300, but overflows to 44; +const auto harmonic_step = freq1 * UFix<8, 0>(12).invAccurate(); // a bit weird way of saying harmonic_step = freq1/12… + + + + +void setup() { + aSin1.setFreq(freq1); // set the frequency with a Q16n16 fractional number + aGain.setFreq(0.2f); // use a float for low frequencies, in setup it doesn't need to be fast + kChangeNoteDelay.set(2000); // note duration ms, within resolution of CONTROL_RATE + startMozzi(); +} + +void updateControl() { + if (kChangeNoteDelay.ready()) { + UFix<4, 0> harmonic = rand((byte)12); + auto shimmer = toSFraction(rand((byte)255)); + auto freq2difference = (harmonic * harmonic_step) + (harmonic_step * shimmer).sR<2>(); + auto freq2 = (freq1 - freq2difference); + aSin2.setFreq(freq2); + kChangeNoteDelay.start(); + } +} + + +/** +@todo Add a constrain for FixMath +*/ +AudioOutput updateAudio() { + int asig = (int)((((uint32_t)aSin1.next() + aSin2.next()) * (200u + aGain.next())) >> 3); + int16_t clipped = constrain(asig, -244, 243); + return clipped; +} + +void loop() { + audioHook(); // required here +} From 5a121dbc5b5c76f3c51d0c37af88655615e3a9d1 Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Wed, 28 Feb 2024 22:16:43 +0100 Subject: [PATCH 14/25] Waveshapper_difference_tone to Mozzi2.0 standard (Where is this example coming from??) --- .../Waveshaper_Difference_Tone/Waveshaper_Difference_Tone.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/06.Synthesis/Waveshaper_Difference_Tone/Waveshaper_Difference_Tone.ino b/examples/06.Synthesis/Waveshaper_Difference_Tone/Waveshaper_Difference_Tone.ino index 95395e85d..acad4dd8c 100644 --- a/examples/06.Synthesis/Waveshaper_Difference_Tone/Waveshaper_Difference_Tone.ino +++ b/examples/06.Synthesis/Waveshaper_Difference_Tone/Waveshaper_Difference_Tone.ino @@ -17,7 +17,7 @@ Tim Barrass 2012, CC by-nc-sa. */ -#include +#include #include #include #include @@ -28,7 +28,7 @@ // use #define for CONTROL_RATE, not a constant -#define CONTROL_RATE 64 // powers of 2 please +#define MOZZI_CONTROL_RATE 64 // powers of 2 please // use: Oscil oscilName (wavetable), look in .h file of table #included above Oscil aSin1(SIN2048_DATA); // sine wave sound source From 75ad03d42acad28f4788353ef526dc3a394bf12e Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Sun, 3 Mar 2024 15:46:57 +0100 Subject: [PATCH 15/25] Corrected Line with FixMath --- Line.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Line.h b/Line.h index 170873155..1aa68cac6 100644 --- a/Line.h +++ b/Line.h @@ -324,7 +324,7 @@ class Line> private: typedef UFix internal_type; internal_type current_value; - internal_type step_size; + SFix step_size; public: /** Constructor. Use the template parameter to set the type of numbers you @@ -361,7 +361,7 @@ class Line> void set(internal_type targetvalue, UFix<_NI,0> num_steps) { if(num_steps.asRaw()) { - internal_type numerator = targetvalue-current_value; + auto numerator = targetvalue-current_value; step_size = numerator*num_steps.invAccurate(); } else { step_size = 0; @@ -378,7 +378,7 @@ class Line> void set(internal_type targetvalue, T num_steps) { if(num_steps) { - internal_type numerator = targetvalue-current_value; + auto numerator = targetvalue-current_value; step_size = internal_type(numerator.asRaw()/num_steps,true); } else { step_size = 0; @@ -407,7 +407,7 @@ class Line> private: typedef SFix internal_type; internal_type current_value; - internal_type step_size; + SFix step_size; public: /** Constructor. Use the template parameter to set the type of numbers you @@ -444,7 +444,7 @@ class Line> void set(internal_type targetvalue, UFix<_NI,0> num_steps) { if(num_steps.asRaw()) { - internal_type numerator = targetvalue-current_value; + auto numerator = targetvalue-current_value; step_size = numerator*num_steps.invAccurate(); } else { step_size = 0; @@ -461,7 +461,7 @@ class Line> void set(internal_type targetvalue, T num_steps) { if(num_steps) { - internal_type numerator = targetvalue-current_value; + auto numerator = targetvalue-current_value; step_size = internal_type(numerator.asRaw()/num_steps,true); } else { step_size = 0; From be74b3bde2cb69fd5474041327b610f66d1df48b Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Sun, 3 Mar 2024 16:02:10 +0100 Subject: [PATCH 16/25] Changed Line_vs_Smooth example to FixMath --- .../Line_vs_Smooth/Line_vs_Smooth.ino | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/examples/05.Control_Filters/Line_vs_Smooth/Line_vs_Smooth.ino b/examples/05.Control_Filters/Line_vs_Smooth/Line_vs_Smooth.ino index 4d88d3c3e..69da1c958 100644 --- a/examples/05.Control_Filters/Line_vs_Smooth/Line_vs_Smooth.ino +++ b/examples/05.Control_Filters/Line_vs_Smooth/Line_vs_Smooth.ino @@ -6,7 +6,7 @@ updated in the background while audio generation continues, and the most recent readings can be read anytime from an array. Also demonstrates linear interpolation with Line(), - filtering with Smooth(), and fixed point numbers. + filtering with Smooth(), and fixed point numbers from FixMath Circuit: Audio output on digital pin 9 (for standard output on a Uno or similar), or @@ -22,14 +22,14 @@ Mozzi help/discussion/announcements: https://groups.google.com/forum/#!forum/mozzi-users - Tim Barrass 2013, CC by-nc-sa. + Tim Barrass 2013, Thomas Combriat and the Mozzi team 2024, CC by-nc-sa. */ #define MOZZI_CONTROL_RATE 64 // Hz, powers of 2 are most reliable #include #include #include // sine table for oscillator -#include +#include #include #include #include @@ -40,13 +40,13 @@ Oscil aSin1(SIN2048_DATA); // Line to interpolate frequency for aSin0. -// Q16n16 is basically (yourNumber << 16) +// UFix<16,16>(yourNumber) is basically encoded in 16bits +// with 16 extra bits for additionnal precision. // Line needs the small analog input integer values of 0-1023 // to be scaled up if the time (the number of steps) is big // enough that distance/time gives a step-size of 0. -// Then you need floats (which are sometimes too slow and create glitches), -// or fixed point. Q16n16: ugly but good. -Line aInterpolate; +// Then you need floats (which are sometimes too slow and create glitches). +Line > aInterpolate; // the number of audio steps the line has to take to reach the next control value const unsigned int AUDIO_STEPS_PER_CONTROL = MOZZI_AUDIO_RATE / MOZZI_CONTROL_RATE; @@ -60,8 +60,8 @@ Smooth aSmooth(smoothness); // to smooth frequency for aSin1 void setup(){ - aSin0.setFreq(660.f); - aSin1.setFreq(220.f); + aSin0.setFreq(660); + aSin1.setFreq(220); startMozzi(); } @@ -69,20 +69,21 @@ void setup(){ volatile unsigned int freq1; // global so it can be used in updateAudio, volatile to stop it getting changed while being used void updateControl(){ - Q16n16 freq0 = Q16n0_to_Q16n16(mozziAnalogRead(0)); // 0 to 1023, scaled up to Q16n16 format + UFix<16,16> freq0 = mozziAnalogRead(0); // 0 to 1023, with an additionnal 16bits of precision (which will be used in the interpolation.) freq1 = (unsigned int) mozziAnalogRead(1); // 0 to 1023 - aInterpolate.set(freq0, AUDIO_STEPS_PER_CONTROL); } AudioOutput updateAudio(){ - Q16n16 interpolatedFreq = aInterpolate.next(); // get the next linear interpolated freq - aSin0.setFreq_Q16n16(interpolatedFreq); + UFix<16,16> interpolatedFreq = aInterpolate.next(); // get the next linear interpolated freq + aSin0.setFreq(interpolatedFreq); int smoothedFreq = aSmooth.next(freq1); // get the next filtered frequency aSin1.setFreq(smoothedFreq); - return MonoOutput::fromNBit(9, (int) (aSin0.next() + aSin1.next())); + + // Here we add to SFix numbers, created from the Oscil, for the output. Mozzi knows what is the final range of this allowing for auto-scaling. + return MonoOutput::fromSFix(toSFraction(aSin0.next()) + toSFraction(aSin1.next())); // auto-scaling of the output. } From 7b2b1d3e840b45671db5888152b9f7f423555d2e Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Sun, 3 Mar 2024 16:45:46 +0100 Subject: [PATCH 17/25] Modified Waveshaper example to FixMath --- .../06.Synthesis/Waveshaper/Waveshaper.ino | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/06.Synthesis/Waveshaper/Waveshaper.ino b/examples/06.Synthesis/Waveshaper/Waveshaper.ino index dfff9408b..16ad966e5 100644 --- a/examples/06.Synthesis/Waveshaper/Waveshaper.ino +++ b/examples/06.Synthesis/Waveshaper/Waveshaper.ino @@ -2,7 +2,7 @@ using Mozzi sonification library. Demonstrates the use of WaveShaper(), EventDelay(), Smooth(), - rand(), and fixed-point numbers. + rand(), and FixMath. Circuit: Audio output on digital pin 9 on a Uno or similar, or DAC/A14 on Teensy 3.1, or @@ -14,7 +14,7 @@ Mozzi help/discussion/announcements: https://groups.google.com/forum/#!forum/mozzi-users - Tim Barrass 2012, CC by-nc-sa. + Tim Barrass 2012, Thomas Combriat and the Mozzi team 2024, CC by-nc-sa. */ #include @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -43,12 +44,11 @@ WaveShaper aCompress(WAVESHAPE_COMPRESS_512_TO_488_DATA); // to compress i EventDelay kChangeNoteDelay; // for random notes -Q8n0 octave_start_note = 42; -Q24n8 carrier_freq; // unsigned long with 24 integer bits and 8 fractional bits +UFix<7,0> octave_start_note = 42; // smooth transitions between notes -Smooth kSmoothFreq(0.85f); -int target_freq, smoothed_freq; +Smooth > kSmoothFreq(0.85f); +UFix<14,2> target_freq, smoothed_freq; //Optimization to have the frequencies on 16bits only. void setup(){ @@ -88,12 +88,12 @@ void updateControl(){ // change octave to midi 24 or any of 3 octaves above octave_start_note = (rand((byte)4)*12)+36; } - Q16n16 midi_note = Q8n0_to_Q16n16(octave_start_note+rndPentatonic()); - target_freq = Q16n16_to_Q16n0(Q16n16_mtof(midi_note)); // has to be 16 bits for Smooth + auto midi_note = octave_start_note + toUInt(rndPentatonic()); + target_freq = mtof(midi_note); // mtof return a UFix<16,16>, which is casted to UFix<14,2> (could overflow if the frequency is greater than 16kHz) kChangeNoteDelay.start(); } - smoothed_freq = kSmoothFreq.next(target_freq*4); // temporarily scale up target_freq to get better int smoothing at low values - aSin.setFreq(smoothed_freq/4); // then scale it back down after it's smoothed + smoothed_freq = kSmoothFreq.next(target_freq); // temporarily scale up target_freq to get better int smoothing at low values + aSin.setFreq(smoothed_freq); } From 8b760a6d5f753760b75b8cdf28e7f633a6d2a767 Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Mon, 4 Mar 2024 21:29:38 +0100 Subject: [PATCH 18/25] Put FM_synth_FixMath to Mozzi2.0 Fix FixMath Removed comment --- .../FMsynth_FixMath/FMsynth_FixMath.ino | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/06.Synthesis/FMsynth_FixMath/FMsynth_FixMath.ino b/examples/06.Synthesis/FMsynth_FixMath/FMsynth_FixMath.ino index 552ec2bba..1cf0e13ac 100644 --- a/examples/06.Synthesis/FMsynth_FixMath/FMsynth_FixMath.ino +++ b/examples/06.Synthesis/FMsynth_FixMath/FMsynth_FixMath.ino @@ -13,10 +13,11 @@ Mozzi help/discussion/announcements: https://groups.google.com/forum/#!forum/mozzi-users - Thomas Combriat and the Mozzi team 2023, CC by-nc-sa. + Tim Barrass 2012, Thomas Combriat and the Mozzi team 2023, CC by-nc-sa. */ - +#include +#define MOZZI_CONTROL_RATE 256 // Hz, powers of 2 are most reliable #include #include @@ -27,11 +28,10 @@ #include -#define CONTROL_RATE 256 // Hz, powers of 2 are most reliable -Oscil aCarrier(COS2048_DATA); -Oscil aModulator(COS2048_DATA); -Oscil kModIndex(COS2048_DATA); +Oscil aCarrier(COS2048_DATA); +Oscil aModulator(COS2048_DATA); +Oscil kModIndex(COS2048_DATA); UFix<0, 16> mod_index; UFix<16, 16> deviation; @@ -47,7 +47,7 @@ EventDelay kNoteChangeDelay; const UFix<7, 0> note_upper_limit = 50, note_lower_limit = 32; -UFix<7, 0> note0 = note_lower_limit, note1 = note_lower_limit + UFix<7, 0>(5), target_note = note0; +UFix<7, 0> note0 = note_lower_limit, note1 = note_lower_limit + UFix<7, 0>(5), target_note = 0; SFix<2, 0> note_change_step = 3; // will only take +3 or -3, 2bits are enough for that UFix<7,8> smoothed_note; @@ -57,7 +57,7 @@ Smooth> kSmoothNote(0.95f); void setup() { kNoteChangeDelay.set(768); kModIndex.setFreq(.768f); // sync with kNoteChangeDelay - startMozzi(CONTROL_RATE); + startMozzi(); } void setFreqs(UFix<7, 8> midi_note) { @@ -92,7 +92,7 @@ void updateControl() { } -AudioOutput_t updateAudio(){ +AudioOutput updateAudio(){ auto modulation = (deviation * toSFraction(aModulator.next())); return MonoOutput::from8Bit(aCarrier.phMod(modulation)); } From 57f94f42218bbb1d314617437edfd19278c24f1e Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Mon, 4 Mar 2024 22:18:17 +0100 Subject: [PATCH 19/25] Modified FMSynth_32k to FixMath Removed comment --- .../FMsynth_32k_HIFI/FMsynth_32k_HIFI.ino | 115 ++++++++---------- .../FMsynth_FixMath/FMsynth_FixMath.ino | 5 + 2 files changed, 57 insertions(+), 63 deletions(-) diff --git a/examples/06.Synthesis/FMsynth_32k_HIFI/FMsynth_32k_HIFI.ino b/examples/06.Synthesis/FMsynth_32k_HIFI/FMsynth_32k_HIFI.ino index ee7a9bc50..367b3d69c 100644 --- a/examples/06.Synthesis/FMsynth_32k_HIFI/FMsynth_32k_HIFI.ino +++ b/examples/06.Synthesis/FMsynth_32k_HIFI/FMsynth_32k_HIFI.ino @@ -3,7 +3,7 @@ Demonstrates Oscil::phMod() for phase modulation, Smooth() for smoothing control signals, - and Mozzi's fixed point number types for fractional frequencies. + and FixMath fixed point number types for fractional frequencies. This sketch using HIFI mode is not for Teensy 3.1. @@ -39,93 +39,82 @@ Mozzi help/discussion/announcements: https://groups.google.com/forum/#!forum/mozzi-users - Tim Barrass 2012-13, CC by-nc-sa. + Tim Barrass 2012-13, Thomas Combriat and the Mozzi team 2024, CC by-nc-sa. */ #include #define MOZZI_AUDIO_MODE MOZZI_OUTPUT_2PIN_PWM #define MOZZI_AUDIO_RATE 32768 -#define MOZZI_CONTROL_RATE 256 // Hz, powers of 2 are most reliable +#define MOZZI_CONTROL_RATE 256 // Hz, powers of 2 are most reliable #include #include -#include // table for Oscils to play +#include // table for Oscils to play #include -#include +#include #include #include + + Oscil aCarrier(COS2048_DATA); Oscil aModulator(COS2048_DATA); Oscil kModIndex(COS2048_DATA); -// The ratio of deviation to modulation frequency is called the "index of modulation". ( I = d / Fm ) -// It will vary according to the frequency that is modulating the carrier and the amount of deviation. -// so deviation d = I Fm -// haven't quite worked this out properly yet... +UFix<0, 16> mod_index; +UFix<8, 16> deviation; // 8 so that we do not exceed 32bits in updateAudio -Q8n8 mod_index;// = float_to_Q8n8(2.0f); // constant version -Q16n16 deviation; +UFix<16, 16> carrier_freq, mod_freq; +const UFix<0,16> modulation_amp = 0.001; // how much the modulation index will vary around its mean +const UFix<2,0> mean_modulation_unscaled = 2; // the real mean modulation will be mean_modulation_unscaled * modulation_max + // this one is adding a bias to the oscillator, hence it should be bigger than one. -Q16n16 carrier_freq, mod_freq; - -// FM ratio between oscillator frequencies, stays the same through note range -Q8n8 mod_to_carrier_ratio = float_to_Q8n8(3.f); +const UFix<2, 0> mod_to_carrier_ratio = 3; // 3 fits in UFix<2,0> which has a maximum range of (2^2)-1=3 EventDelay kNoteChangeDelay; -// for note changes -Q7n8 target_note, note0, note1, note_upper_limit, note_lower_limit, note_change_step, smoothed_note; - -// using Smooth on midi notes rather than frequency, -// because fractional frequencies need larger types than Smooth can handle -// Inefficient, but...until there is a better Smooth.... -Smooth kSmoothNote(0.95f); - -void setup(){ - kNoteChangeDelay.set(768); // ms countdown, taylored to resolution of MOZZI_CONTROL_RATE - kModIndex.setFreq(.768f); // sync with kNoteChangeDelay - target_note = note0; - note_change_step = Q7n0_to_Q7n8(3); - note_upper_limit = Q7n0_to_Q7n8(50); - note_lower_limit = Q7n0_to_Q7n8(32); - note0 = note_lower_limit; - note1 = note_lower_limit + Q7n0_to_Q7n8(5); +const UFix<7, 0> note_upper_limit = 50, note_lower_limit = 32; + +UFix<7, 0> note0 = note_lower_limit, note1 = note_lower_limit + UFix<7, 0>(5), target_note = 0; +SFix<2, 0> note_change_step = 3; // will only take +3 or -3, 2bits are enough for that + +UFix<7,8> smoothed_note; + +Smooth> kSmoothNote(0.95f); + +void setup() { + kNoteChangeDelay.set(768); + kModIndex.setFreq(.768f); // sync with kNoteChangeDelay startMozzi(); } -void setFreqs(Q8n8 midi_note){ - carrier_freq = Q16n16_mtof(Q8n8_to_Q16n16(midi_note)); // convert midi note to fractional frequency - mod_freq = ((carrier_freq>>8) * mod_to_carrier_ratio) ; // (Q16n16>>8) Q8n8 = Q16n16, beware of overflow - deviation = ((mod_freq>>16) * mod_index); // (Q16n16>>16) Q8n8 = Q24n8, beware of overflow - aCarrier.setFreq_Q16n16(carrier_freq); - aModulator.setFreq_Q16n16(mod_freq); +void setFreqs(UFix<7, 8> midi_note) { + carrier_freq = mtof(midi_note); + mod_freq = UFix<14,16>(carrier_freq) * mod_to_carrier_ratio; + deviation = ((UFix<16,0>(mod_freq)) * mod_index).sR<8>(); // the sR here cheaply divides the deviation by 256. + + aCarrier.setFreq(carrier_freq); + aModulator.setFreq(mod_freq); } -void updateControl(){ - // change note - if(kNoteChangeDelay.ready()){ - if (target_note==note0){ - note1 += note_change_step; - target_note=note1; - } - else{ - note0 += note_change_step; - target_note=note0; +void updateControl() { + if (kNoteChangeDelay.ready()) { + if (target_note == note0) + { + note1 = note1 + note_change_step; + target_note = note1; + } + else + { + note0 = note0 + note_change_step; + target_note = note0; } - - // change direction - if(note0>note_upper_limit) note_change_step = Q7n0_to_Q7n8(-3); - if(note0note_upper_limit) note_change_step = -3; + if(note0(); // the sR is to scale back in pure fractional range - // vary the modulation index - mod_index = (Q8n8)350+kModIndex.next(); - - // here's where the smoothing happens smoothed_note = kSmoothNote.next(target_note); setFreqs(smoothed_note); @@ -133,11 +122,11 @@ void updateControl(){ AudioOutput updateAudio(){ - Q15n16 modulation = deviation * aModulator.next() >> 8; - return MonoOutput::from8Bit(aCarrier.phMod(modulation)); // Internally still only 8 bits, will be shifted up to 14 bits in HIFI mode +auto modulation = (deviation * toSFraction(aModulator.next())); +return MonoOutput::from8Bit(aCarrier.phMod(modulation)); } -void loop(){ - audioHook(); -} +void loop() { + audioHook(); +} \ No newline at end of file diff --git a/examples/06.Synthesis/FMsynth_FixMath/FMsynth_FixMath.ino b/examples/06.Synthesis/FMsynth_FixMath/FMsynth_FixMath.ino index 1cf0e13ac..0001c27ae 100644 --- a/examples/06.Synthesis/FMsynth_FixMath/FMsynth_FixMath.ino +++ b/examples/06.Synthesis/FMsynth_FixMath/FMsynth_FixMath.ino @@ -7,6 +7,11 @@ This is the same technique than the FMsynth example but using FixMath instead of mozzi_fixmath. + Note that it is slightly less optimized (but runs fine on an + Uno) in order to make the underlying algorithm clearer. + An optimized version, using FixMath can be found in the + example FMsynth_32k_HIFI. + Mozzi documentation/API https://sensorium.github.io/Mozzi/doc/html/index.html From be0a9b0ae7fc8437d22ff6e0fd47d582716a1eed Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Mon, 4 Mar 2024 23:06:23 +0100 Subject: [PATCH 20/25] Attempt to fix the actions --- .../Waveshaper_Difference_Tone.ino | 86 ------------------- .../Waveshaper_Difference_Tone_FixMath.ino | 79 ----------------- 2 files changed, 165 deletions(-) delete mode 100644 examples/06.Synthesis/Waveshaper_Difference_Tone/Waveshaper_Difference_Tone.ino delete mode 100644 examples/06.Synthesis/Waveshaper_Difference_Tone_FixMath/Waveshaper_Difference_Tone_FixMath.ino diff --git a/examples/06.Synthesis/Waveshaper_Difference_Tone/Waveshaper_Difference_Tone.ino b/examples/06.Synthesis/Waveshaper_Difference_Tone/Waveshaper_Difference_Tone.ino deleted file mode 100644 index acad4dd8c..000000000 --- a/examples/06.Synthesis/Waveshaper_Difference_Tone/Waveshaper_Difference_Tone.ino +++ /dev/null @@ -1,86 +0,0 @@ -/* Example using waveshaping to modify the spectrum of an audio signal - using Mozzi sonification library. - - Demonstrates the use of WaveShaper(), EventDelay(), Smooth(), - rand(), and fixed-point numbers. - - Note that a similar example but using the newer FixMath - library is also available in Waveshaper_Difference_Tone_FixMath. - - Circuit: Audio output on digital pin 9 on a Uno or similar, or - DAC/A14 on Teensy 3.1, or - check the README or http://sensorium.github.com/Mozzi/ - - Mozzi help/discussion/announcements: - https://groups.google.com/forum/#!forum/mozzi-users - - Tim Barrass 2012, CC by-nc-sa. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - - -// use #define for CONTROL_RATE, not a constant -#define MOZZI_CONTROL_RATE 64 // powers of 2 please - -// use: Oscil oscilName (wavetable), look in .h file of table #included above -Oscil aSin1(SIN2048_DATA); // sine wave sound source -Oscil aSin2(SIN2048_DATA); // sine wave sound source -Oscil aGain(SIN2048_DATA); // to fade audio signal in and out before waveshaping - -// for scheduling note changes -EventDelay kChangeNoteDelay; - -// audio frequency as Q16n16 fractional number -Q16n16 freq1 = Q8n0_to_Q16n16(300); - - - - -void setup(){ - startMozzi(CONTROL_RATE); // set a control rate of 64 (powers of 2 please) - aSin1.setFreq_Q16n16(freq1); // set the frequency with a Q16n16 fractional number - aGain.setFreq(0.2f); // use a float for low frequencies, in setup it doesn't need to be fast - kChangeNoteDelay.set(2000); // note duration ms, within resolution of CONTROL_RATE -} - - -void updateControl(){ - if(kChangeNoteDelay.ready()){ - // change proportional frequency of second tone - byte harmonic = rand((byte)12); - byte shimmer = rand((byte)255); - Q16n16 harmonic_step = freq1/12; - Q16n16 freq2difference = harmonic*harmonic_step; - freq2difference += (harmonic_step*shimmer)>>11; - Q16n16 freq2 = freq1-freq2difference; - aSin2.setFreq_Q16n16(freq2); // set the frequency with a Q16n16 fractional number - kChangeNoteDelay.start(); - } -} - - -int updateAudio(){ - int asig = (int)((((uint32_t)aSin1.next()+ aSin2.next())*(200u+aGain.next()))>>3); - int clipped = constrain(asig,-244,243); - return clipped; -} - - -void loop(){ - audioHook(); // required here -} - - - - - - - diff --git a/examples/06.Synthesis/Waveshaper_Difference_Tone_FixMath/Waveshaper_Difference_Tone_FixMath.ino b/examples/06.Synthesis/Waveshaper_Difference_Tone_FixMath/Waveshaper_Difference_Tone_FixMath.ino deleted file mode 100644 index 9144846cf..000000000 --- a/examples/06.Synthesis/Waveshaper_Difference_Tone_FixMath/Waveshaper_Difference_Tone_FixMath.ino +++ /dev/null @@ -1,79 +0,0 @@ -/* Example using waveshaping to modify the spectrum of an audio signal - using Mozzi sonification library. - - Nearly verbatim translation from Waveshaper_Difference_Tone example - but using using FixMath instead of mozzi_fixmath. - - Demonstrates the use of WaveShaper(), EventDelay(), Smooth(), - rand(), and fixed-point numbers using FixMath. - - Circuit: Audio output on digital pin 9 on a Uno or similar, or - DAC/A14 on Teensy 3.1, or - check the README or http://sensorium.github.com/Mozzi/ - - Mozzi help/discussion/announcements: - https://groups.google.com/forum/#!forum/mozzi-users - - Tim Barrass 2012, Thomas Combriat and the Mozzi team 2024, CC by-nc-sa. -*/ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -// use #define for CONTROL_RATE, not a constant -#define MOZZI_CONTROL_RATE 64 // powers of 2 please - -// use: Oscil oscilName (wavetable), look in .h file of table #included above -Oscil aSin1(SIN2048_DATA); // sine wave sound source -Oscil aSin2(SIN2048_DATA); // sine wave sound source -Oscil aGain(SIN2048_DATA); // to fade audio signal in and out before waveshaping - -// for scheduling note changes -EventDelay kChangeNoteDelay; - -const UFix<6, 0> freq1 = 44; // original example says 300, but overflows to 44; -const auto harmonic_step = freq1 * UFix<8, 0>(12).invAccurate(); // a bit weird way of saying harmonic_step = freq1/12… - - - - -void setup() { - aSin1.setFreq(freq1); // set the frequency with a Q16n16 fractional number - aGain.setFreq(0.2f); // use a float for low frequencies, in setup it doesn't need to be fast - kChangeNoteDelay.set(2000); // note duration ms, within resolution of CONTROL_RATE - startMozzi(); -} - -void updateControl() { - if (kChangeNoteDelay.ready()) { - UFix<4, 0> harmonic = rand((byte)12); - auto shimmer = toSFraction(rand((byte)255)); - auto freq2difference = (harmonic * harmonic_step) + (harmonic_step * shimmer).sR<2>(); - auto freq2 = (freq1 - freq2difference); - aSin2.setFreq(freq2); - kChangeNoteDelay.start(); - } -} - - -/** -@todo Add a constrain for FixMath -*/ -AudioOutput updateAudio() { - int asig = (int)((((uint32_t)aSin1.next() + aSin2.next()) * (200u + aGain.next())) >> 3); - int16_t clipped = constrain(asig, -244, 243); - return clipped; -} - -void loop() { - audioHook(); // required here -} From f7a40f2ef89af5d96495d4ccadda5608df2c7d93 Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Tue, 5 Mar 2024 21:09:23 +0100 Subject: [PATCH 21/25] Adapted AMsynth_HIFI to FixMath --- .../AMsynth_HIFI/AMsynth_HIFI.ino | 86 +++++++++---------- 1 file changed, 41 insertions(+), 45 deletions(-) diff --git a/examples/06.Synthesis/AMsynth_HIFI/AMsynth_HIFI.ino b/examples/06.Synthesis/AMsynth_HIFI/AMsynth_HIFI.ino index 0ccc9139b..566c5a5a4 100644 --- a/examples/06.Synthesis/AMsynth_HIFI/AMsynth_HIFI.ino +++ b/examples/06.Synthesis/AMsynth_HIFI/AMsynth_HIFI.ino @@ -7,7 +7,7 @@ values, random numbers with rand(), and EventDelay() for scheduling. - Important: + Important: This sketch uses MOZZI_OUTPUT_2PIN_PWM (aka HIFI) output mode, which is not available on all boards (among others, it works on the classic Arduino boards, but not Teensy 3.x and friends). @@ -30,25 +30,26 @@ Alternatively using 39 ohm, 4.99k and 470nF components will work directly with headphones. - Mozzi documentation/API - https://sensorium.github.io/Mozzi/doc/html/index.html + Mozzi documentation/API + https://sensorium.github.io/Mozzi/doc/html/index.html - Mozzi help/discussion/announcements: + Mozzi help/discussion/announcements: https://groups.google.com/forum/#!forum/mozzi-users - Tim Barrass 2012-13, CC by-nc-sa. + Tim Barrass 2012, Thomas Combriat and the Mozzi team 2024, CC by-nc-sa. */ + #include #define MOZZI_AUDIO_MODE MOZZI_OUTPUT_2PIN_PWM - #include #include -#include // table for Oscils to play -#include +#include // table for Oscils to play #include #include #include +#include + // audio oscils Oscil aCarrier(COS2048_DATA); @@ -56,76 +57,71 @@ Oscil aModulator(COS2048_DATA); Oscil aModDepth(COS2048_DATA); // for scheduling note changes in updateControl() -EventDelay kNoteChangeDelay; +EventDelay kNoteChangeDelay; -// synthesis parameters in fixed point formats -Q8n8 ratio; // unsigned int with 8 integer bits and 8 fractional bits -Q24n8 carrier_freq; // unsigned long with 24 integer bits and 8 fractional bits -Q24n8 mod_freq; // unsigned long with 24 integer bits and 8 fractional bits +UFix<8, 8> ratio; // unsigned int with 8 integer bits and 8 fractional bits +UFix<24, 8> carrier_freq; // unsigned long with 24 integer bits and 8 fractional bits // for random notes -Q8n0 octave_start_note = 42; - -void setup(){ - ratio = float_to_Q8n8(3.0f); // define modulation ratio in float and convert to fixed-point - kNoteChangeDelay.set(200); // note duration ms, within resolution of MOZZI_CONTROL_RATE - aModDepth.setFreq(13.f); // vary mod depth to highlight am effects - randSeed(); // reseed the random generator for different results each time the sketch runs - startMozzi(); // use default MOZZI_CONTROL_RATE 64 +const UFix<7, 0> octave_start_note = 42; + +void setup() { + ratio = 3; + kNoteChangeDelay.set(200); // note duration ms, within resolution of MOZZI_CONTROL_RATE + aModDepth.setFreq(13.f); // vary mod depth to highlight am effects + randSeed(); // reseed the random generator for different results each time the sketch runs + startMozzi(); } -void updateControl(){ - static Q16n16 last_note = octave_start_note; +void updateControl() { + static auto last_note = octave_start_note; - if(kNoteChangeDelay.ready()){ + if (kNoteChangeDelay.ready()) { // change octave now and then - if(rand((byte)5)==0){ - last_note = 36+(rand((byte)6)*12); + if (rand((byte)5) == 0) { + last_note = UFix<7, 0>(36 + (rand((byte)6) * 12)); } // change step up or down a semitone occasionally - if(rand((byte)13)==0){ - last_note += 1-rand((byte)3); + if (rand((byte)13) == 0) { + last_note = last_note + SFix<7, 0>(1 - rand((byte)3)); } // change modulation ratio now and then - if(rand((byte)5)==0){ - ratio = ((Q8n8) 1+ rand((byte)5)) <<8; + if (rand((byte)5) == 0) { + ratio = 1 + rand((byte)5); } - // sometimes add a fraction to the ratio - if(rand((byte)5)==0){ - ratio += rand((byte)255); + // sometimes add a fractionto the ratio + if (rand((byte)5) == 0) { + ratio = ratio + toUFraction(rand((byte)255)); } // step up or down 3 semitones (or 0) - last_note += 3 * (1-rand((byte)3)); + last_note = last_note + SFix<7, 0>(3 * (1 - rand((byte)3))); // convert midi to frequency - Q16n16 midi_note = Q8n0_to_Q16n16(last_note); - carrier_freq = Q16n16_to_Q24n8(Q16n16_mtof(midi_note)); + carrier_freq = mtof(last_note); // calculate modulation frequency to stay in ratio with carrier - mod_freq = (carrier_freq * ratio)>>8; // (Q24n8 Q8n8) >> 8 = Q24n8 + auto mod_freq = carrier_freq * ratio; // set frequencies of the oscillators - aCarrier.setFreq_Q24n8(carrier_freq); - aModulator.setFreq_Q24n8(mod_freq); + aCarrier.setFreq(carrier_freq); + aModulator.setFreq(mod_freq); // reset the note scheduler kNoteChangeDelay.start(); } } - -AudioOutput updateAudio(){ - unsigned int mod = (128u+ aModulator.next()) * ((byte)128+ aModDepth.next()); - return MonoOutput::fromNBit(24, (long)mod * aCarrier.next()); // 16 bit * 8 bit = 24 bit +AudioOutput updateAudio() { + auto mod = UFix<8, 0>(128 + aModulator.next()) * UFix<8, 0>(128 + aModDepth.next()); + return MonoOutput::fromSFix(mod * toSFraction(aCarrier.next())); } - -void loop(){ +void loop() { audioHook(); } From 74ef27175fae95ef58b43cb637120720b895280d Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Tue, 5 Mar 2024 22:28:06 +0100 Subject: [PATCH 22/25] Updated Shepard tones to FixMath --- .../Shepard_Tone_HIFI/Shepard_Tone_HIFI.ino | 67 +++++++++---------- .../Shepard_Tone_HIFI/triangle512_uint8.h | 41 ------------ 2 files changed, 32 insertions(+), 76 deletions(-) delete mode 100644 examples/12.Misc/Shepard_Tone_HIFI/triangle512_uint8.h diff --git a/examples/12.Misc/Shepard_Tone_HIFI/Shepard_Tone_HIFI.ino b/examples/12.Misc/Shepard_Tone_HIFI/Shepard_Tone_HIFI.ino index 2500ea881..2c5e14e5b 100644 --- a/examples/12.Misc/Shepard_Tone_HIFI/Shepard_Tone_HIFI.ino +++ b/examples/12.Misc/Shepard_Tone_HIFI/Shepard_Tone_HIFI.ino @@ -29,8 +29,10 @@ Mozzi help/discussion/announcements: https://groups.google.com/forum/#!forum/mozzi-users - Tim Barrass 2018, CC by-nc-sa. + Tim Barrass 2018, Thomas Combriat and the Mozzi team 2024, CC by-nc-sa. */ + + #include #define MOZZI_AUDIO_MODE MOZZI_OUTPUT_2PIN_PWM #define MOZZI_CONTROL_RATE 128 @@ -38,29 +40,32 @@ #include #include #include -#include "triangle512_uint8.h" +#include #include #include #include -#include // for fixed-point fractional numbers +#include // reset and sync vol and freq controls each cycle EventDelay kTriggerDelay0; EventDelay kTriggerDelay1; -#define NOTE_CENTRE 60 -#define NOTE_RANGE 12 -#define NOTE_START_FIXEDPOINT float_to_Q16n16(NOTE_CENTRE + NOTE_RANGE) // so Line gliss has enough precision -#define NOTE_END_FIXEDPOINT float_to_Q16n16(NOTE_CENTRE - NOTE_RANGE) // so Line gliss has enough precision +const UFix<7,0> NOTE_CENTRE = 60, NOTE_RANGE = 12; +const UFix<7,0> NOTE_START_FIXEDPOINT = NOTE_CENTRE + NOTE_RANGE; +const UFix<7,0> NOTE_END_FIXEDPOINT = NOTE_CENTRE - NOTE_RANGE; #define GLISS_SECONDS 3.f +//#define CONTROL_STEPS_PER_GLISS ((unsigned int)((float)MOZZI_CONTROL_RATE * GLISS_SECONDS)) #define CONTROL_STEPS_PER_GLISS ((unsigned int)((float)MOZZI_CONTROL_RATE * GLISS_SECONDS)) + // use: Line lineName -// audio oscils -Line kGliss0; // Line to slide frequency -Line kGliss1; // Line to slide frequency +// The lines needs more precision than the notes, as they interpolate in between +// here we optimize by choosing the max precision to stay in 16bits (7+9=16) +// note that for an SFix, we would aim for 15 (plus a sign bit). +Line > kGliss0; // Line to slide frequency +Line > kGliss1; // Line to slide frequency // harmonics Oscil aCos0(SIN8192_DATA); @@ -71,12 +76,12 @@ Oscil kVol0(TRIANGLE512_DATA); Oscil kVol1(TRIANGLE512_DATA); // audio volumes updated each control interrupt and reused in audio -long v0, v1; +SFix<0,14> v0, v1; +//SFix<0,7> v0, v1; // micro-optimization void setup() { - //Serial.begin(115200); +//Serial.begin(115200); - // set volume change frequencies kVol0.setFreq(0.5f / GLISS_SECONDS); kVol1.setFreq(0.5f / GLISS_SECONDS); @@ -89,9 +94,8 @@ void setup() { startMozzi(); } - -void updateControl() { - +void updateControl() +{ if (kTriggerDelay0.ready()) { kGliss0.set(NOTE_START_FIXEDPOINT, NOTE_END_FIXEDPOINT, CONTROL_STEPS_PER_GLISS); kVol0.setPhase(0); @@ -99,39 +103,32 @@ void updateControl() { Serial.println("gliss1"); } - if (kTriggerDelay1.ready()) { + if (kTriggerDelay1.ready()) { kGliss1.set(NOTE_START_FIXEDPOINT, NOTE_END_FIXEDPOINT, CONTROL_STEPS_PER_GLISS); kVol1.setPhase(0); kTriggerDelay1.start((int)(GLISS_SECONDS * 1000.f)); // milliseconds Serial.println("\t gliss2"); } - // set harmonic frequencies - Q16n16 gliss0 = kGliss0.next(); // fixed point - float freq0 = Q16n16_to_float(Q16n16_mtof(gliss0)); // convert fixed point to floating point - - Q16n16 gliss1 = kGliss1.next(); // fixed point - float freq1 = Q16n16_to_float(Q16n16_mtof(gliss1)); // convert fixed point to floating point + auto gliss0 = kGliss0.next(); // fixed point + auto gliss1 = kGliss1.next(); // fixed point - aCos0.setFreq(freq0); - aCos1.setFreq(freq1); + aCos0.setFreq(mtof(gliss0)); + aCos1.setFreq(mtof(gliss1)); - v0 = kVol0.next(); - v1 = kVol1.next(); - // Serial.print((byte)v0); Serial.print("\t"); Serial.println((byte)v1); + v0 = toSFraction(kVol0.next()); // convert as a pure fractionnal between -1 and 1 + v1 = toSFraction(kVol1.next()); - // square for perceptual volume - v0 *= v0; - v1 *= v1; + // square for perceptual volume (also makes it positive...) + v0 = v0 * v0; + v1 = v1 * v1; } AudioOutput updateAudio() { - long asig = ((v0 * aCos0.next()) >> 8) + - ((v1 * aCos1.next()) >> 8); - return AudioOutput::fromNBit(17, asig); + auto asig = v0 * toSInt(aCos0.next()) + v1 * toSInt(aCos1.next()); + return AudioOutput::fromSFix(asig); // auto-scaling of the output with SFix } - void loop() { audioHook(); } diff --git a/examples/12.Misc/Shepard_Tone_HIFI/triangle512_uint8.h b/examples/12.Misc/Shepard_Tone_HIFI/triangle512_uint8.h deleted file mode 100644 index bd3d3e7c4..000000000 --- a/examples/12.Misc/Shepard_Tone_HIFI/triangle512_uint8.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef TRIANGLE512_H_ -#define TRIANGLE512_H_ - -#include -#include "mozzi_pgmspace.h" - -#define TRIANGLE512_NUM_CELLS 512 -#define TRIANGLE512_SAMPLERATE 512 - -CONSTTABLE_STORAGE(uint8_t) TRIANGLE512_DATA [] = {0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, -30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, -50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, -70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, -90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, -108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, -124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, -140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, -156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, -172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, -188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, -204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, -220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, -236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, -252, 253, 254, 255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, -242, 241, 240, 239, 238, 237, 236, 235, 234, 233, 232, 231, 230, 229, 228, 227, -226, 225, 224, 223, 222, 221, 220, 219, 218, 217, 216, 215, 214, 213, 212, 211, -210, 209, 208, 207, 206, 205, 204, 203, 202, 201, 200, 199, 198, 197, 196, 195, -194, 193, 192, 191, 190, 189, 188, 187, 186, 185, 184, 183, 182, 181, 180, 179, -178, 177, 176, 175, 174, 173, 172, 171, 170, 169, 168, 167, 166, 165, 164, 163, -162, 161, 160, 159, 158, 157, 156, 155, 154, 153, 152, 151, 150, 149, 148, 147, -146, 145, 144, 143, 142, 141, 140, 139, 138, 137, 136, 135, 134, 133, 132, 131, -130, 129, 128, 127, 126, 125, 124, 123, 122, 121, 120, 119, 118, 117, 116, 115, -114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 99, -98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, -78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, -58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, -38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, -18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, }; - - #endif /* TRIANGLE512_H_ */ From 273f2c044449a2020d824990b3156a8cba5368e6 Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Sun, 10 Mar 2024 16:58:08 +0100 Subject: [PATCH 23/25] Corrected Line_vs_Smooth example --- .../05.Control_Filters/Line_vs_Smooth/Line_vs_Smooth.ino | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/05.Control_Filters/Line_vs_Smooth/Line_vs_Smooth.ino b/examples/05.Control_Filters/Line_vs_Smooth/Line_vs_Smooth.ino index 69da1c958..d7ed21df2 100644 --- a/examples/05.Control_Filters/Line_vs_Smooth/Line_vs_Smooth.ino +++ b/examples/05.Control_Filters/Line_vs_Smooth/Line_vs_Smooth.ino @@ -66,16 +66,17 @@ void setup(){ } -volatile unsigned int freq1; // global so it can be used in updateAudio, volatile to stop it getting changed while being used +unsigned int freq1; // global so it can be used in updateAudio void updateControl(){ UFix<16,16> freq0 = mozziAnalogRead(0); // 0 to 1023, with an additionnal 16bits of precision (which will be used in the interpolation.) freq1 = (unsigned int) mozziAnalogRead(1); // 0 to 1023 + aInterpolate.set(freq0, AUDIO_STEPS_PER_CONTROL); } AudioOutput updateAudio(){ - UFix<16,16> interpolatedFreq = aInterpolate.next(); // get the next linear interpolated freq + auto interpolatedFreq = aInterpolate.next(); // get the next linear interpolated freq aSin0.setFreq(interpolatedFreq); int smoothedFreq = aSmooth.next(freq1); // get the next filtered frequency From e8376db995e3429196e73dfd3d0f6d2d9f8f0e61 Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Sun, 10 Mar 2024 17:02:24 +0100 Subject: [PATCH 24/25] Changed Detuned beats examples to Mozzi2 --- .../Detuned_Beats_Wash/Detuned_Beats_Wash.ino | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/examples/06.Synthesis/Detuned_Beats_Wash/Detuned_Beats_Wash.ino b/examples/06.Synthesis/Detuned_Beats_Wash/Detuned_Beats_Wash.ino index 7960c5971..cc3881db1 100644 --- a/examples/06.Synthesis/Detuned_Beats_Wash/Detuned_Beats_Wash.ino +++ b/examples/06.Synthesis/Detuned_Beats_Wash/Detuned_Beats_Wash.ino @@ -37,22 +37,22 @@ #include // harmonics -Oscil aCos1(COS8192_DATA); -Oscil aCos2(COS8192_DATA); -Oscil aCos3(COS8192_DATA); -Oscil aCos4(COS8192_DATA); -Oscil aCos5(COS8192_DATA); -Oscil aCos6(COS8192_DATA); -//Oscil aCos7(COS8192_DATA); // used to work smoothly in Arduino 1.05 +Oscil aCos1(COS8192_DATA); +Oscil aCos2(COS8192_DATA); +Oscil aCos3(COS8192_DATA); +Oscil aCos4(COS8192_DATA); +Oscil aCos5(COS8192_DATA); +Oscil aCos6(COS8192_DATA); +//Oscil aCos7(COS8192_DATA); // used to work smoothly in Arduino 1.05 // duplicates but slightly off frequency for adding to originals -Oscil aCos1b(COS8192_DATA); -Oscil aCos2b(COS8192_DATA); -Oscil aCos3b(COS8192_DATA); -Oscil aCos4b(COS8192_DATA); -Oscil aCos5b(COS8192_DATA); -Oscil aCos6b(COS8192_DATA); -//Oscil aCos7b(COS8192_DATA); +Oscil aCos1b(COS8192_DATA); +Oscil aCos2b(COS8192_DATA); +Oscil aCos3b(COS8192_DATA); +Oscil aCos4b(COS8192_DATA); +Oscil aCos5b(COS8192_DATA); +Oscil aCos6b(COS8192_DATA); +//Oscil aCos7b(COS8192_DATA); // base pitch frequencies in Q16n16 fixed int format (for speed later) UFix<12,15> f1,f2,f3,f4,f5,f6;//,f7; From 574fb90a932bc3f7f3c2d3ca68c12c83fee524e1 Mon Sep 17 00:00:00 2001 From: tomcombriat Date: Sun, 10 Mar 2024 17:34:35 +0100 Subject: [PATCH 25/25] Optimization of mtof for non fractionnal FixMath midi values. --- mozzi_midi.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/mozzi_midi.h b/mozzi_midi.h index 94dd04e2b..5e9d49abc 100644 --- a/mozzi_midi.h +++ b/mozzi_midi.h @@ -13,6 +13,12 @@ class MidiToFreqPrivate { friend int mtof(uint8_t); friend int mtof(int); friend Q16n16 Q16n16_mtof(Q16n16); + template + friend UFix<16,16> mtof(UFix); + + template + friend UFix<16,16> mtof(SFix); + static CONSTTABLE_STORAGE(uint32_t) midiToFreq[128]; }; @@ -143,4 +149,22 @@ inline UFix<16,16> mtof(SFix midival) return UFix<16,16>::fromRaw(Q16n16_mtof(UFix<16,16>(midival).asRaw())); }; +/** @ingroup midi +Converts *whole* midi note number with speed and accuracy (more accurate that mtof(uint8_t)) +*/ +template +inline UFix<16,16> mtof(UFix midival) +{ + return UFix<16,16>::fromRaw((FLASH_OR_RAM_READ(MidiToFreqPrivate::midiToFreq + midival.asRaw()))); +}; + +/** @ingroup midi +Converts *whole* midi note number with speed and accuracy (more accurate that mtof(uint8_t)) +*/ +template +inline UFix<16,16> mtof(SFix midival) +{ + return UFix<16,16>::fromRaw((FLASH_OR_RAM_READ(MidiToFreqPrivate::midiToFreq + midival.asUFix().asRaw()))); +}; + #endif /* MOZZI_MIDI_H_ */