Real sounds are very complex compared to the clean version described by mathematical treatises. Even analogue synthesisers produce overtones the frequency of which tend to be exact multiples of the fundamental and which are phase locked (0 degrees of the fundamental is 0 degrees on all of of the overtones). The human voice is much more complex than this. Whilst it produces overtones then are not always exact and the fundamental is not exact and the overtones move over one another. Without all this complexity the resulting sound is 'thin'. This lack of complexity is one of the reasons, I believe, that sample based synthesis (which is based on real sounds) has dominated over true synthesis (creation from first principles) in recent decades.
With the rise in very powerful but affordable general purpose computing, additive synthesis has come into reach. I started to work on this technique with Sonic Field, but it was inefficient. Consider this piece:
Whilst the synthesis is quite voice like, producing the piece required tens of hours of compute time. To help reduce the cost by increasing the efficiency I have been working on a C++ additive synthesis system where the power of the C++ compiler is used to optimised the synthesis call graph. However, the original Sonic Field synthesiser added randomness to get the complex sounds. [Please note that one could do the same with the JVM but it would require going beyond what the javac compiler can do 'out the box'.]
Rather than just produce the fundamental and it overtones, many fundamentals of slightly differing frequencies were overlaid. Not only this, but the overtones of each of those fundamentals varied, using a pseudo random number generator, from their exact ratio to the fundamental. To achieve the same effect with the compile time optimiser required making some sort of pseudo random generator at compile time.
OK, this is a bit of a hack. If you are offended by things not done in an exhaustively thorough way, do not continue reading!
The trick is that we generate a 'random' number from a integer; i.e. the input integer 1 will produce a very different output number than input 2. The input numbers should not be connected in any way which be detectable from the resulting audio. This is why simple things like lookup tables can fail because the length of the table tends to alias with the sample rate of the resulting audio in surprising ways.
We do not need a full double precision random number, just a few significant figures will do. As we are doing template programming we need that number in integral form. Further, the number needs to be bounded. So, to achieve this I use the pseudo random (ish) way the sin function of integers changes in a restricted space. This is slightly floored as the sin function approaches 0 and 180 degrees. However, if (for example) we take 12 significant figures (decimal) and then use modulus to restrict the field to just the bottom 6 the resulting conversion from integer (radians) to a 6 figure 'random' number is plenty good enough for the audio work here.
Because gcc is kind enough to allow sin() in a constexpr context, the above code works just fine; however, I did need to give the compiler a hand by separating out the modulus and sin() steps into different functions.
As I describehere
I have implemented the placement and intensity of overtones (harmonics) of a note using a template of constexpr functions. This is a simple encapsulation which can be resolved at compile time; further, a set of static asserts is great for ensuring that the compile time restriction applies.[Note in a later post I will discuss how that static assert shows the compile time restriction can apply but does not guarantee the compiler applies it. I also show a way of forcing compile time resolution.]
Making sure constexpr really are compile time resolvable using static_assert.
So now the randomness is injected based on a template argument which is an integer. I can thus overlay many slightly different notes using a template expansion which gives a different integer to each instantiation of the note generating template:
This is slightly flawed at the moment as the same series of integers will be used each time. I should inject a final 'offset' integer which increases globally as templates are expended so that each 'random' is different. Alternatively some trick around file name and line number could be used. I will give this some thought.
Anyhow, here is a template which uses the above templates to overlay 10 slightly different versions of the same note:
If you are curious as to if all this works, well it does. In the next two posts I will take this technology to the point where it can create the underlying 'feed stock' of a sung note. Then, after than, will come post processing etc.