Create waveform and play it like an oscillator, wavetable synthesis

Hey there,

I am a bit stuck in the variety of audio extensions for openframeworks and I am not sure which way is best for my needs.
I simply like to generate a waveform from different data sources and than use this form as base for an oscilator. I think you call it then wavetable synthesis. Additionaly to that i like to be able to play this sound slower and faster. In PureData I would use Tabread4 for that but what should I use in OpenFrameworks? Which Library should I use? Has someone experiences in this direction? What would you use?

Hi @rak_multi

Maybe if you are familiar with Pd you could use :

https://github.com/danomatika/ofxPd .

This allows you to load Pd patches inside OF with a lot of flexibility and even integration inside mobile devices …
Another nice reference on audio synthesis is a Maximilian port :

https://github.com/micknoise/Maximilian/tree/master/ofxMaxim

hope it helps …

if your on mac you can use https://github.com/Ahbee/ofxStk. It can definitely work for other platforms though. You use the FileLoop class, it basically takes a file and loops it at any frequency. If you need any help let me know.

Hey guys,
thanks for your answers. I had a look to all of the three mentioned addons/ways.
What i don’t like about ofxPd is that it is somehow based on an existing PD patch and therefore is not so flexible like everything is created in realtime. Thats one of the main disadvantages of PD in my opinion. It is possible to generate patch structures in realtime but it is more or less not really object oriented and optimised. But maybe i am not right about this.
In ofxMaxim i don’t really see a way of loading a custom wavetable in runtime.
At the moment ofxStk maybe seems the best way. Is there an alternative to fileLoop which can load and resize a wavetable from virtual data? I mean not from a wav file. I like to fill an variable sized array with values and play it. But maybe I schould generate wav files first and then load and play them…

You can load stk raw files. There are the easiest to create at run time, save to file,and then read from them.

STK RAW files have no header and are assumed to contain a monophonic 
stream of 16-bit signed integers in big-endian byte order at a sample 
rate of 22050 Hz.

There might be a way to skip the save to disk part. And read straight from STKFrames. I’ll look into it. I’m coming out a new example that will demonstrate the use of a wavetable and adsr. It should be out in the next day or two.I’ll let you know. Also if you wanna know about saving wav,files to disk you can look at this thread Recording audio (ios)?. Its for ios but the same code should work on a mac.

actually you can use the FIleWvOut class of stk to write audio to disk. Much easier than the link I providied you.

This is a great discussion and I’ve been playing with all these different addons for the past few months now, aside from the ofxStk which I’m extremely interested in. In my experience I’ve ended up using the main ofSoundPlayer (as creatively as possible), ofxAudioUnits, and other times just end up sending OSC messages between max, pd, or ableton. I will say however, if there is some plug in that does exactly what you want, it’s worth downloading it as an audio unit. The ofxAudioUnits addon gives you quite a bit of control over audio units. Hope this helps.

Yes, I should test how fast writing a wav or raw data file to disk and than read this again. that would give me much more freedom in the selection of tools to read the data again I think. By the way, I am not on mac, I am on linux. but STK is no problem on linux. The audioUnit thingy therefore is not working I think.

I had another look to the ofxTonic addon. i think is almost exactly what i am searching for: https://github.com/TonicAudio/Tonic/blob/master/examples/Demo%20Synths/ArbitraryTableLookupDemo.cpp

especcialy this line:

TableLookupOsc osc = TableLookupOsc().setLookupTable(lookupTable).freq( 100 + 40 * SineWave().freq(0.1) );

I really like the style of tonic. and the simple way of patching. I will give it a try.

@rak_multi I updated ofxStk to include a wavetable example. Its pretty easy to implement. You can read about here

Woah, @Ahbee, that is awsome! It compiled out of the box on linux and runs like a charm :).
Thats a great startingpoint for my project. Thank you! I am sure I maybe have some questions when I look deeper into the code. But for now I am wishless happy ;).

By the way, I compiled ofxTonic but it only gave me strange sounds so far.

If you have questions about stk or the examples please ask away.

fixed a bug in the adsr of drawsynth should be working now.

@Ahbee, thanks for the update. something was a bit strange before with the attack and release. I had now time to look deeper into the code. I am trying to understand the ofApp::getFramesFromWave() function.
Do I get it right, in this function the wave polyline gets converted and interpolated to fit into the samplerate sized wavetable?
By the way this interpolation function from STK is great :).
I changed the the wavetable size to different values that seems to work well. do you think it is better to use a divisor of the samplerate as the size for the wavetable size? or multiples of 8?
Is it important to have the table samplerate sized for accurate frequency controll?
And another thing is, am i right that you are setting the end point to the same value then the starting point to prevent clicks?

Maybe I should answer a part of my question myself :). I tried different things and I think I figured it out. Surely it is the same as in PD.
It is not necessary to use the bitrate as the table size. But if I not do this I have to correct the frequency of the oscillator to
compensate this if I want accurate pitch.

So this brings me to the following formula to adjust the frequency to play the table in accurate speed.

adjusted-freq (HZ) = desired-frequency (HZ) * bitrate / table-size

Yeah this function takes the drawing and creates a 44100 frame wavetable;

stk::StkFrames ofApp::getFramesFromWave(){
    int numberOfFrames = stk::Stk::sampleRate();
    stk::StkFrames frames(numberOfFrames,1);
    for (int i = 0; i < numberOfFrames; i++) {
        float xValue = ofMap(i, 0, numberOfFrames-1, drawRegion.x, drawRegion.x+drawRegion.width);
        const vector<ofPoint> &points = wave.getVertices();
        float lerpPercentage;
        float firstVal;
        float secondVal;
        float yValue;
        for (int j = 0; j < wave.size()-1; j++) {
            firstVal = points[j].x;
            secondVal = points[j+1].x;
            if (xValue >= firstVal && xValue <=secondVal) {
                lerpPercentage = (xValue-firstVal)/(secondVal - firstVal);
                ofPoint newPoint = points[j].interpolated(points[j+1], lerpPercentage);
                yValue = transformYValue(newPoint.y);
                break;
            }
        }
        frames[i] = yValue;
    }
    return frames;
}

It does not have to be to be 44100. youn can change this line

int numberOfFrames = stk::Stk::sampleRate();

to

int numberOfFrames = 5000;

and it should should still work. In fact 5000 is probably best for drawSynth. Yes higher numbers mean more accuracy,but the wave drawing only has about 500 pixel to work with, so dividing it up 44100 times might be unaccurate.

if you change the table Size, you should not have to mess with the the frequency. Not sure what that formula you posted up there is(do you mean sample rate instead of bit rate?). But you look at the tick function it looks like this:

StkFloat DrawSynth::tick(unsigned int channel){
    static float index = 0;
    int tableSize = waveTable.getTableSize();
    float frqTL = (float)tableSize/Stk::sampleRate();
    float indexIncremet = frqTL * frequency;
    lastFrame_[0] = waveTable.tick(index);
    index+=indexIncremet;
    if (index >= tableSize ) {
        index = 0;
    }
    lastFrame_[0]*= adsr.tick();
    lastFrame_[0]*=gain;
    return lastFrame_[0];
}

so the index increment is already adjusted based on the table size.

I also did not make the endPoints equal,but that is something you should do, to prevent clicks. what I did was just make sure the wave drawing is a function (1 yValue for each xValue);

It is weird that you have to adjust the frequency, because you shouldn’t have to, can you post code?

Hey there,
thanks for your explanations. I had this thing with the pitch actually when I bypassed the mapping functionality in the getFramesFromWave() function. I tried this because I want to try how I can use a not interpolated table.
My formular then have should look like following. I meant samplerate not bitrate:
adjusted-freq (HZ) = desired-frequency (HZ) * stk::Stk::sampleRate() / sizeOfVectorIWantToSonify

When I want to use more raw data which is not interpolated I maybe should adjust the StkFloat DrawSynth::tick function.
I will try that.

The getFramesFromWave() is only useful if if you are trying to convert a drawing to wavetable. Which I assume is not what you are trying to do. If you you already if have a wavetable, all you have to do is represent it as StkFrames and call DrawSynth::setWaveTable(const stk::StkFrames &frames) and everything should work. No need to mess with frequencies. The waveable can be any size,but it must represent exactly one period of the wave.

If you can give more context on what you are tyring to do. I can help out a more.

I simply like to audify different data, like gps tracks graphs and so on. The drawing feature will also be used but in an other project. It maybe sounds strange but I like to use even really small vectors as wave table, like vectors with a size of 9 or so but with the ability to smoothen the wave. for that I maybe use a bandpass or maybe memos ofxMSAInterpolator addon. I am fully aware of the noise that such a raw and maybe small table would produce.

Are you making sure your your wave table is exactly one period long? Just convert your 9 element wavetable into an STKFrames object with 9 frames and one channel. then call DrawSynth::setWaveTable(const stk::StkFrames &frames), It should play just fine. no need to change frequency or interpolate anything.