Sounds like putting the horse *in* the cart and pulling it yourself to me. What if the carrier and modulator frequencies aren't related by small integers?duncanparsons wrote:Nice idea, that.. might try itMadBrain wrote:By the way, for "anti-aliased" FM there's an easier and faster way - if you do the FM synthesis at a sample rate that's a multiple of the note freq, the reflected frequencies should line up with the frequencies already present, so you won't get the "metallic" sound of alias.After that you just resample the output to the target sample rate (ie 44.1khz) using your favourite algo.
![]()
Alias Free Oscs, band limiting, and downsampling
-
- KVRAF
- 2460 posts since 3 Oct, 2002 from SF CA USA NA Earth
-
- KVRAF
- 8389 posts since 11 Apr, 2003 from back on the hillside again - but now with a garden!
-
- KVRAF
- 2460 posts since 3 Oct, 2002 from SF CA USA NA Earth
-
Music Engineer Music Engineer https://www.kvraudio.com/forum/memberlist.php?mode=viewprofile&u=15959
- KVRAF
- 4379 posts since 8 Mar, 2004 from Berlin, Germany
How about the cpu-usage? i mean, when i use one wavetable per octave, i would have to do a branching in my audio loop like this (i assume to render one sample at a time):
wouldn't this if-branch be less efficient, than to render always -say- 4 samples and then lowpass-filtering and decimating them to one sample? especially for higher notes, where the function jumps out of the branch very late? i know, that one could choose the table not for each single sample for one block of samples. but in this case, no pitch modulation would be possible (i like synthesizers having pitch-envelopes - my Aggressor-synth for instance provides a pitch ramp for each of it's 3 oscillators - so i need to update the osc's freq each sample). also FM requires the freq to be updated sample by sample...
Code: Select all
Osc::getSample()
{
if(freq<55)
//use table1
else if(freq<110)
//use table2
else if(freq<220)
//use table3
else if(freq<440)
//use table4
//and so on
}- u-he
- 30192 posts since 8 Aug, 2002 from Berlin
BrainDoc...
When you're using one table per octave... use an array of pointers to the wavetables. Use the exponent of a frequency value to determine the table.
Urs
When you're using one table per octave... use an array of pointers to the wavetables. Use the exponent of a frequency value to determine the table.
-
- KVRAF
- 2460 posts since 3 Oct, 2002 from SF CA USA NA Earth
Assuming you blend between wavetables as the pitch changes instead of doing a hard cut between them, you can select some compromise blocksize (32 or 64 samples) to select the table -- as long as your pitch modulation doesn't get very deep and very fast, you'll rarely have any audible aliasing. If the modulation is too fast for that to work, then you actually have FM, which leads me to...braindoc wrote:i know, that one could choose the table not for each single sample for one block of samples. but in this case, no pitch modulation would be possible (i like synthesizers having pitch-envelopes - my Aggressor-synth for instance provides a pitch ramp for each of it's 3 oscillators - so i need to update the osc's freq each sample).
Strictly speaking, the spectrum generated by FM or PM always has unlimited harmonics of nonzero amplitude, so aliasing is guaranteed regardless of what wavetable you select. Yay!also FM requires the freq to be updated sample by sample...
The only way I can think of to avoid it is to synthesize one cycle of the modulated FM signal (either with brute force in the time domain or using Urs's IFFT strategy from the predicted spectrum) and use that as the wavetable; this doesn't allow you to have inharmonic partials. (It's also essentially equivalent to MadBrain's suggestion, now that I think about it.) So in fact if you want completely alias free FM, you may as well go straight to additive synthesis.
If instead you decide you can accept some aliasing in your FM, then pick a wavetable that is an acceptable compromise between aggressive brightness (more partials, more aliasing) and mellow dullness (fewer partials, less aliasing).
-
- KVRAF
- 8389 posts since 11 Apr, 2003 from back on the hillside again - but now with a garden!
-
Music Engineer Music Engineer https://www.kvraudio.com/forum/memberlist.php?mode=viewprofile&u=15959
- KVRAF
- 4379 posts since 8 Mar, 2004 from Berlin, Germany
the "exponent of a frequency value" value confused me a little bit at first and i was thinking like: 55^x - huh? ...110^y - häh? but after sleeping a night over it, i think understand now what you mean. i could do something like:Urs wrote:BrainDoc...
When you're using one table per octave... use an array of pointers to the wavetables. Use the exponent of a frequency value to determine the table.
Urs
tableIndex = log2(increment)
where increment is the current increment in my lookup-table and tableIndex is the index of the table which should be used - as the result of the log2 is a noninteger, i could use floor(tableIndex) to determine the actual table or use the fractional part for a crossfade between two tables. when the increment is 1, i use table[0], when it is 2 - table[1], when it is 4 - table[2]... and so on, where each of the successive tables contains only half of the spectrum of the previous one. was that your idea? however, i will implement and test this procedure right now. if it works fine (which is, what i expect), i'll use it for my aggressor-synth.
-
- KVRian
- 922 posts since 26 Mar, 2003 from Guildford, England
I think he meant that:braindoc wrote: the "exponent of a frequency value" value confused me a little bit at first and i was thinking like: 55^x - huh? ...110^y - häh? but after sleeping a night over it, i think understand now what you mean. i could do something like:
tableIndex = log2(increment)
where increment is the current increment in my lookup-table and tableIndex is the index of the table which should be used - as the result of the log2 is a noninteger, i could use floor(tableIndex) to determine the actual table or use the fractional part for a crossfade between two tables. when the increment is 1, i use table[0], when it is 2 - table[1], when it is 4 - table[2]... and so on, where each of the successive tables contains only half of the spectrum of the previous one. was that your idea? however, i will implement and test this procedure right now. if it works fine (which is, what i expect), i'll use it for my aggressor-synth.
index = log2(frequency).
since a float is an exponent and a mantissa, you can extract the exponent directly as an integer and use it as a table index (with appropriate masking to prevent
out of bounds access etc). This gets rid of the log2, since you aren't interested in the significand. when calculating the table index:
Code: Select all
const int EXPONENT_BIAS = 127;
const int EXPONENT_MASK = 0x7C00000;
const int EXPONENT_OFFSET = 23;
const unsigned long ifrq = *(reinterpret_cast<unsigned long const* const>(&frequency));
index = ((ifrq & EXPONENT_MASK) >> EXPONENT_OFFSET) - EXPONENT_BIAS;
-
Music Engineer Music Engineer https://www.kvraudio.com/forum/memberlist.php?mode=viewprofile&u=15959
- KVRAF
- 4379 posts since 8 Mar, 2004 from Berlin, Germany
aha. makes sense, although it always seems quite complicated to me to deal with these low-level things. i'll try it with log2, and if i got that to work, i'll think about optimizing it with your suggestion.texture wrote:
...
This gets rid of the log2, since you aren't interested in the significand. when calculating the table index:
i think..Code: Select all
const int EXPONENT_BIAS = 127; const int EXPONENT_MASK = 0x7C00000; const int EXPONENT_OFFSET = 23; const unsigned long ifrq = *(reinterpret_cast<unsigned long const* const>(&frequency)); index = ((ifrq & EXPONENT_MASK) >> EXPONENT_OFFSET) - EXPONENT_BIAS;
-
- KVRian
- 922 posts since 26 Mar, 2003 from Guildford, England
Actually, my calc was slightly wrong, it shudl be:
Code: Select all
index = ((ifrq >> 23) & 0xFF) - 127
-
Music Engineer Music Engineer https://www.kvraudio.com/forum/memberlist.php?mode=viewprofile&u=15959
- KVRAF
- 4379 posts since 8 Mar, 2004 from Berlin, Germany
i just wrote a little function, which can extract the exponent from a float-value - i think, it can be made more efficient, but for clarity of the code, i post it, as it is:
...maybe someone will find it useful. i will now go on and write a similar thing for doubles (which is, what i need). ....i hate this low-level stuff!...
EDIT: it looks a little bit ugly-formatted here, because of the wraparounds of the comments
Code: Select all
unsigned long getExponentFromFloat(float valueFlt)
{
static unsigned long bitMask = 0x7FFFFFFF; //unsigned 32 bit integer
//leftmost bit is 0, all others are 1
static unsigned long exponent = 0; //this is the variable which should finally hold
//the exponent
static unsigned long *ptrInt; //pointer to a 32-bit integer
static void *ptr; //pointer to to anything
ptr = &valueFlt; //determine the adress of the float-variable
ptrInt = (unsigned long*) ptr; //store this adress in the int-pointer
exponent = *ptrInt; //get the value and interpret is as int
//mask out the sign-bit via bitwise and:
exponent = exponent & bitMask;
//rightshift by 23 to shift the mantissa away:
exponent = exponent >> 23;
//subtract the bias:
exponent -= 127;
return exponent;
}EDIT: it looks a little bit ugly-formatted here, because of the wraparounds of the comments
-
- KVRian
- 922 posts since 26 Mar, 2003 from Guildford, England
which can be reduced to:
By the way, the statics in your version could be made const, which means that the compiler will optinmize them away.
Code: Select all
typedef unsigned int uint32;
inline uint32 getExponentFromFloat(float f)
{
return (((*(reinterpret_cast<uint32 const* const>(&f)) >> 23) & 0xFF) - 127;
}
-
Music Engineer Music Engineer https://www.kvraudio.com/forum/memberlist.php?mode=viewprofile&u=15959
- KVRAF
- 4379 posts since 8 Mar, 2004 from Berlin, Germany
yes. i just wanted to post something, where it can easily be seen, what actually happens. but for my synth, i think, i will aslo use such a reduced version.
-
Music Engineer Music Engineer https://www.kvraudio.com/forum/memberlist.php?mode=viewprofile&u=15959
- KVRAF
- 4379 posts since 8 Mar, 2004 from Berlin, Germany
do you refer to this paper here?:Urs wrote:
Hard Sync doesn't need oversampling to keep aliasing inaudible, even when using wavetables. I posted the algorithm I use for hard sync (Zebra, FilterscapeVA) on musicdsp mailing list, roughly a year ago. In the end, clever tuning can do the trick![]()
http://www.cs.cmu.edu/~eli/papers/icmc01-hardsync.pdf
if not, could you post a link here to the algorithm?
BTW: i have my osc's almost alias-free now with a one table per octave switch, where the table number is deteremined by the exponent of the phase-increment - the additional CPU-load is very low. i'm VERY happy with that! thanks for the hint! maybe it's now time to get the sync alias-free, too


