Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Specializations of Mozzi classes for U/SFix #236

Merged
merged 27 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
285795e
Added specializations to Line for U/SFix
tomcombriat Feb 18, 2024
59b1778
Added U/SFix specialization to Smooth.h
tomcombriat Feb 18, 2024
ebca5ab
Added an example of FM synthesis with FixMath
tomcombriat Feb 19, 2024
9e1c4ad
Removed volatile in Line.h
tomcombriat Feb 19, 2024
8d69afc
Made a more specific template in Oscil for FixMath
tomcombriat Feb 24, 2024
5f2f36e
Added Difference_Tone adapted to FixMath
tomcombriat Feb 24, 2024
b088666
Adapted Detuned_Beats_Wash example
tomcombriat Feb 25, 2024
cfe7973
Merge branch 'devel/Mozzi2' into devel/mozziClass_FixMath
tomcombriat Feb 25, 2024
05ef0f1
Added outputs from SFix for MonoOutput and StereoOutput
tomcombriat Feb 26, 2024
dbed60a
Added automatic scaling to DBW example
tomcombriat Feb 26, 2024
672ce21
More specific templates for Oscil and mozzi_midi
tomcombriat Feb 26, 2024
4b483dd
SFix actual number of bits is one extra compared to said size (which
tomcombriat Feb 26, 2024
a52b44e
Adapted AMSynth example to FixMath
tomcombriat Feb 28, 2024
1730643
Adapted Waveshapper_Difference_Tone with FixMath
tomcombriat Feb 28, 2024
5a121db
Waveshapper_difference_tone to Mozzi2.0 standard
tomcombriat Feb 28, 2024
75ad03d
Corrected Line with FixMath
tomcombriat Mar 3, 2024
be74b3b
Changed Line_vs_Smooth example to FixMath
tomcombriat Mar 3, 2024
7b2b1d3
Modified Waveshaper example to FixMath
tomcombriat Mar 3, 2024
8b760a6
Put FM_synth_FixMath to Mozzi2.0
tomcombriat Mar 4, 2024
57f94f4
Modified FMSynth_32k to FixMath
tomcombriat Mar 4, 2024
be0a9b0
Attempt to fix the actions
tomcombriat Mar 4, 2024
f7a40f2
Adapted AMsynth_HIFI to FixMath
tomcombriat Mar 5, 2024
74ef271
Updated Shepard tones to FixMath
tomcombriat Mar 5, 2024
273f2c0
Corrected Line_vs_Smooth example
tomcombriat Mar 10, 2024
e8376db
Changed Detuned beats examples to Mozzi2
tomcombriat Mar 10, 2024
574fb90
Optimization of mtof for non fractionnal FixMath midi values.
tomcombriat Mar 10, 2024
4f00936
Merge branch 'devel/Mozzi2' into devel/mozziClass_FixMath
tomcombriat Apr 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion AudioOutput.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@

#ifndef AUDIOOUTPUT_H
#define AUDIOOUTPUT_H
#include <FixMath.h>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a somewhat general note, I wonder, whether we can still avoid a hard dependency on the - now external - FixMath. Sure, installing it via the library manager should be a breeze, but at least when installing from git, that may be a small bonus.

The trick would be to never have a full specialization. Due to the RANGE parameter, I think that requirement is actually easy to meet. Pseudocode:

//#include <FixMath.h> -> removed
template<int8_z, int8_t, uint64_t> class SFix;  // forward declaration, only

// This partial specialization is only compiled when actually used
// therefore it's ok that SFix is an incomplete type, so far.
template<int8_t NF, uint64_t RANGE> void someFunction(SFix<16,NF,RANGE> x) { [...] };

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And to clarify, FixMath.h would then have to be included, manually, in sketches that actually use it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good idea, I did not think of it. My only concern would be that the dependency is less clear (ie, the error message would be something like: SFix is declared but never defined and not File FixMath.h not found). But at the same time, the error message for the examples would be clear. Will try to see how that goes!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be overlooking something, but I am not sure this is always simple. Let's take this line from Oscil.h:

        template <int8_t NI, int8_t NF, uint64_t RANGE>
        inline
        void setFreq(UFix<NI,NF,RANGE> frequency)
        {
        setFreq_Q16n16(UFix<16,16>(frequency).asRaw());
        }

with the forward declaration setFreq(UFix<NI,NF,RANGE> frequency) is correctly resolved by the compiler but UFix<16,16>(frequency) is not as it does not have the correct number of arguments. Adding another argument, say _RANGE, is not straightforward because we would need to add in Mozzi the macros to compute the default range here (hence negating a bit the advantage of having it with a default value in FixMath.

Did I overlooked something?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did I overlooked something?

Only the fact that I tend to comment before testing... It may or may not be possible to weasle our way out of this particular problem using something like

setFreq_Q16n16((int32_t) frequency.sL(16-NF).asRaw());

But I'm not sure, where the next problem of this type pops up, and I'll admit, I'm not totally convinced, myself.


/** 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
Expand Down Expand Up @@ -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<int8_t NI, int8_t NF, uint64_t RANGE>
static inline MonoOutput fromSFix(SFix<NI,NF,RANGE> 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.
*
Expand Down Expand Up @@ -168,6 +172,9 @@ template<typename T> 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<int8_t NI, int8_t NF, uint64_t RANGE, int8_t _NI, int8_t _NF, uint64_t _RANGE>
static inline StereoOutput fromSFix(SFix<NI,NF,RANGE> 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<typename A, typename B> static inline StereoOutput fromAlmostNBit(A bits, B l, B r) { return StereoOutput(SCALE_AUDIO_NEAR(l, bits), SCALE_AUDIO_NEAR(r, bits)); }
private:
Expand Down
183 changes: 178 additions & 5 deletions Line.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

#include <Arduino.h>

#include<FixMath.h>

/** 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
Expand All @@ -29,12 +31,15 @@ represent fractional numbers. Google "fixed point arithmetic" if this is new to
you.
*/




template <class T>
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
Expand Down Expand Up @@ -108,7 +113,7 @@ template <>
class Line <unsigned char>
{
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:
Expand Down Expand Up @@ -177,7 +182,7 @@ template <>
class Line <unsigned int>
{
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:
Expand Down Expand Up @@ -249,7 +254,7 @@ template <>
class Line <unsigned long>
{
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:
Expand Down Expand Up @@ -311,6 +316,174 @@ class Line <unsigned long>
}
};


/* UFix specialisation */
template<int8_t NI, int8_t NF>
class Line<UFix<NI, NF>>
{
private:
typedef UFix<NI, NF> 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 \<int\> 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<int8_t _NI>
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<typename T>
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<typename T>
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<int8_t NI, int8_t NF>
class Line<SFix<NI, NF>>
{
private:
typedef SFix<NI, NF> 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 \<int\> 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<int8_t _NI>
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<typename T>
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<typename T>
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.
Expand Down
6 changes: 3 additions & 3 deletions Oscil.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<NI,NF> fixed-point number format.
*/
template <int8_t NI, int8_t NF>
template <int8_t NI, int8_t NF, uint64_t RANGE>
inline
void setFreq(UFix<NI,NF> frequency)
void setFreq(UFix<NI,NF,RANGE> frequency)
{
setFreq_Q16n16(UFix<16,16>(frequency).asRaw());
setFreq_Q16n16(UFix<16,16>(frequency).asRaw());
}


Expand Down
Loading
Loading