## Some questions about wavetable synthesis?

clau_ste
KVRer

26 posts since 20 Jan, 2017
I'm tring to create simple wavetable oscillation, what i want to do and what i have tried to is this:
Created an array of double, for example
Code: Select all
`double sinWavetable[1024]`

Filled this array of doubles with a simple for loop
Code: Select all
`for (int i = 0; i < 1024; i++) {     double x = resized(i);     sinWavetable[i] = sin(x);}`

And this is the resized function which "scale" the value i from 0-1024 to 0-6.28 (twoPI)
Code: Select all
`inline double resized(int x) { return (twoPI *  x) / 1024; };`

How could i read this waveform at different notes, for exampe if i press note C3 in my midi keyboard, how could i output a sine wave at the correct frequency? This is something i found hard to understand even if maybe it's a really simple mechanism
DJ Warmonger
KVRAF

2667 posts since 7 Jun, 2012, from Warsaw
It's called direct digital synthesis.

http://www.analog.com/media/en/training ... MT-085.pdf

Just ignore everything about the analog part.
http://djwarmonger.wordpress.com/
Tricky-Loops wrote: (...)someone like Armin van Buuren who claims to make a track in half an hour and all his songs sound somewhat boring(...)
whyterabbyt
Beware the Quoth

25878 posts since 3 Sep, 2001, from R'lyeh Oceanic Amusement Park and Funfair
In short, every sample/clock tick, increment an index through your wavetable dependent on the sample rate versus the note frequency.

ie If you want the sample to have a frequency of 1Hz, you have to read through the table incrementally once per second. If your sample rate were 1024Hz, for table size of 1024, then every clock 'tick' your index into the table would be incremented by 1, so that in a second you read each value in the wavetable once.
However if your sample rate was 2048Hz, then to play back at 1Hz, still be reading through the table once, but the index would have to be incremented by 0.5 (ie 1024/2048) each clock 'tick'... etc etc

If you want the sample to have a frequency of 2Hz, you have to read through the table twice per second... In that case, if your sample rate were 1024Hz and your table size was 1024, then every clock 'tick' your index into the table would be incremented by 2, modulo the table size to wrap round it again.
etc etc

Since the index increments are almost certainly not going to be integers, then you will probably be wanting to access values 'in between' those encoded in the table. These would probably be interpolated.
"The bearer of this signature is a genuine and authorised pope."
clau_ste
KVRer

26 posts since 20 Jan, 2017
So i need to calculate the correct index which will get two near values from the wavetable?
As this?

Let's say that i pass to this method 65.41 which is a C2 note

Code: Select all
`double currentStep = 0.0;function double getWTValueFromFrequency(double frequency) {    double freqIfStepIs1 = sampleRate / wtLenght; //Example 44.100 / 1024 = 43.066 (Hz)    double step = currentStep + (frequency / freqIfStepIs1); //Example 65.41 / 43.066 = 1.5188 -> step with frequency passed    double value = interpolate(step);    currentStep = step;}function double interpolate(double step) {    int stepInt = (int) step; //In this case we get the value of 1 from 1.5188    double val1 = sinWavetable[stepInt]; //if step is 1.5188 we get the value at index 1    double val2 = sinWavetable[stepInt+1]; //store the next value    double percent = (step - stepInt) * 100; //we get 0.5188 out from 1.5188, so multiplying by 100 we get 51.88    return (val1 +  ((val2 - val1) * percent));}`

This function should get the correct value between two near double values from the wavetable, I thinked it correctly?

Maybe it's not implemented well because the are surely better coded function, but the logic is correct?
earlevel
KVRist

456 posts since 4 Apr, 2010
May I suggest my series on wavetable oscillators? It covers a bit more than you're asking (mainly, switching multiple-resolution wavetables, for more complex waveforms without aliasing), but it goes through all the details and includes C++ source code:

http://www.earlevel.com/main/category/digital-audio/oscillators/wavetable-oscillators/?order=asc
My audio DSP blog: earlevel.com
earlevel
KVRist

456 posts since 4 Apr, 2010
clau_ste wrote:Let's say that i pass to this method 65.41 which is a C2 note

I suggest that you run your oscillator in normalized frequency. That is, treat frequency as a floating point value ranging 0-1, where 1 is the sample rate. This is convenient because that value is also your normalized phase increment. In other words, a frequency (and phase increment) of 0.5 means that the table is sampled exactly twice per cycle. The phasor runs 0-1, incremented by the phase increment every sample, wrapping. When you need an output, the phasor value is multiple by the length of your wavetable to produce an index.

Here is the code for my oscillator—you can see how simple and efficient it makes things, while also making frequency calculations obvious and trivial:

Code: Select all
`// note: if you don't keep this in the range of 0-1, you'll need to make changes elsewhereinline void WaveTableOsc::setFrequency(double inc) {    phaseInc = inc;}inline void WaveTableOsc::updatePhase() {    phasor += phaseInc;        if (phasor >= 1.0)        phasor -= 1.0;}`

The sample lookup becomes, essentially, "waveTable[int(phasor * waveTableLen)]"—a bit more to select the appropriate table, dependent on frequency, and any interpolation, but you can see how simple normalized frequency makes things.

For your example of 65.41 Hz, you'd set the oscillator frequency to 65.41 / sampleRate (or 65.41 * samplePeriod). Using normalized frequency also makes it convenient for placing exponential converters in front of the oscillator frequency input.
My audio DSP blog: earlevel.com
clau_ste
KVRer

26 posts since 20 Jan, 2017
Gonna check it but i would also understand how to implment things on my own, as for example, wavetables reading
clau_ste
KVRer

26 posts since 20 Jan, 2017
earlevel wrote:
clau_ste wrote:Let's say that i pass to this method 65.41 which is a C2 note

I suggest that you run your oscillator in normalized frequency. That is, treat frequency as a floating point value ranging 0-1, where 1 is the sample rate. This is convenient because that value is also your normalized phase increment. In other words, a frequency (and phase increment) of 0.5 means that the table is sampled exactly twice per cycle. The phasor runs 0-1, incremented by the phase increment every sample, wrapping. When you need an output, the phasor value is multiple by the length of your wavetable to produce an index.

But if i have a midi keyboard as in this tutorial http://www.martin-finke.de/blog/article ... -keyboard/, how can i have an oscillator that control frequency? I want to send midi messages to my oscillator object and then get the correct sound corresponding to my wavetable
whyterabbyt
Beware the Quoth

25878 posts since 3 Sep, 2001, from R'lyeh Oceanic Amusement Park and Funfair
clau_ste wrote:But if i have a midi keyboard as in this tutorial http://www.martin-finke.de/blog/article ... -keyboard/, how can i have an oscillator that control frequency? I want to send midi messages to my oscillator object and then get the correct sound corresponding to my wavetable

Dont. Do MIDI, note and voice handling outside your oscillator code; pass your oscillator a parameter that directly controls frequency.
Dont bog down your sample-rate operations with extraneous event-rate operations like MIDI note handling.

And consider that on most synths etc, pitch is also a thing that can be controlled 'internally' eg patch-specific static controls such as octave selection or fine-(de)tuning of an oscillator, or via any dynamic parameters like pitch modulation, and also 'externally' via MIDI pitchbend messages. MIDI notes are not the only arbiter of oscillator frequency.
"The bearer of this signature is a genuine and authorised pope."
clau_ste
KVRer

26 posts since 20 Jan, 2017
earlevel wrote:
Code: Select all
`// note: if you don't keep this in the range of 0-1, you'll need to make changes elsewhereinline void WaveTableOsc::setFrequency(double inc) {    phaseInc = inc;}inline void WaveTableOsc::updatePhase() {    phasor += phaseInc;        if (phasor >= 1.0)        phasor -= 1.0;}`

The sample lookup becomes, essentially, "waveTable[int(phasor * waveTableLen)]"—a bit more to select the appropriate table, dependent on frequency, and any interpolation, but you can see how simple normalized frequency makes things.

I understanded this code and it seems clear, i also understanded the logic, but now comes another question, is better to use linear interpolation or spline instead of using "waveTable[int(phasor * waveTableLen)]"? If yes, how more correct is the value? Is the performance of the reading in a way lower?
earlevel
KVRist

456 posts since 4 Apr, 2010
clau_ste wrote:I understanded this code and it seems clear, i also understanded the logic, but now comes another question, is better to use linear interpolation or spline instead of using "waveTable[int(phasor * waveTableLen)]"? If yes, how more correct is the value? Is the performance of the reading in a way lower?

Please go through my series of articles, including the video. I address this in great detail, including audio examples.

The bottom line is that the success of any kind of interpolation (including zero-order) depends also on the resolution of the table—how many real samples you have, how oversampled your table is). Linear interpolation is a big improvement over no interpolation, at a very low cost. The reason to use higher-order interpolation (spline, etc.) would be because your tables are small. There's little reason for that running on a computer with loads of memory.
My audio DSP blog: earlevel.com

Moderator: Moderators (Main)