Wavetable - How to create1 cycle wavetable tutorial w. examples


#21

Hey @matthewcieplak

Nice. Wavetables are the best :smile: I never use regular oscillators anymore. Only wavetables :smile:

I just checked a few of the adventure kid 1 cycle waves and they are all 600 samples long each and they are 16 bit 44.1khz. That is great. They can be used in different ways in Axoloti:

  1. This is in my opinion the best way.
    Open up Audacity and load up one of the 600 samples long waves.
    Next highlight the whole file and go to effect and choose change speed.
    Change the speed to Axolotis closest table size in SAMPLES. That would be 512 samples. Then you know for a FACT that the file fit perfectly in the table.

  2. This is the easiest way, since you already changed the files to raw and it would be a hassle to go back to Audacity and strecth all one by one. But this is NOT perfect and will definatly create clicks in many situations, cause it is not always 100% precise like first way I suggested.
    The solution is to calcualte how many percentage of the table/alloc the raw file actually fills up.
    You do that this way. For this you HAVE to set tabelsize to be bigger than the file:
    Tablesize = 1024 samples
    .raw adventure kid samplesize = 600 samples

Dividide the .raw files samplesize with tables:
600/1027 = 0,5859375 which is 58,59375 %

You see not precise. This percentage would be rounded to 58,59% in Axoloti. Anyway to set it up this way you would have to connect an attentuator between the phasor osc and the table/play object and set the attenuator to 58.59%

It is a solution, but I wouldnt use it. I would personally go back and change the speed of every single file individually. That way you get the best result. And even better, just make it a habit doing them one by one. Wavetables are very different and for example for especially multi wavetablesl Serums morhped wavetable will not work in axoloti(as for now), cause they basicly only consist of 4-8 waves. So if you try to use a wavetable that was morphed and you are not aware it wont sound right. Also a lot of the wavetable pack out there, the individual files are not same size, length etc. Som there are god reason to go through them manually. It can be a lot of work, but very rewarding when done :wink:

About the morph waves. I believe it can be done with Axoloti as it is now. I already build a wavetable creator patch which you can check out here. Need to do the final work on it before i can share final version with saving included. Anyway, check the patch here and a small video that shows it in action :smile:

But yeah, got a few ideas how to implement morphed waves in a more controlled fashion than the one from the link. WOuld like to make a version which moprhes a few waves at a time. For example start out as a saw wave and moprh into a sine wave. Actually think it can be done with the one I posed already now that I think of it.


#22

mhehehehehe, this is sweet:
1. download AKWF waveforms (4358 different waveforms), "advanced renamer" and audacity v2 (v1 crashes!)

  1. throw all the AKWF waveforms in the same map (they come in different maps) and rename them using Advanced Renamer to W0000 to W4358.

  2. Load the waveforms into audacity (my computer only handles 500 at a time, nope, re-edit (again), you'll probably have to restart the computer after around 1000 files to clear the cache....sighs)

  3. set audacity internal rate to 48K

  4. resample all waveforms to 48K (this may take quite a while)

  5. adjust the playback speed: percentage= -36.23 for 1024 samples length

  6. go to "export multiple", select "other uncompressed files" and set to Raw (headerless), signed 16-bit-PCM

  7. now you'll need to press down the enter button a couple of minutes, to press "yes" for each file repeatedly (when closing in on the last file, go easy, otherwise it might restart the whole saving process and you'll need to go around one more time haha)

now you've got 4358 raw waveforms to build yourself a massive wavetable oscillator :stuck_out_tongue:
Use the index string module, enter W0, W1, W2, W3 or W4 in the name, or load 5 index string modules with W0 to W4 as their names and use a mux to select one of them, Raw as extension and use the index to select any of the waveforms (000 to 999). Remember though.. W4 "only" has 358 samples under it's hood.. :wink:

Another edit:
I've just send all the waveforms to "Adventure Kid", so he can post them on his website. So just wait a while and you can just download them instead of having them all manually redo them yourself (I don't think I'm allowed to upload them myself)


#23

Some remarks:
* With a one-cycle wavetable, the sample rate of the wave is irrelevant
* I strongly suspect sample rate conversion in audacity (and other similar software) assumes silence before and after the wave, while wavetables should be assumed cyclic.
* The right way change the length of wavetables is going through (unwindowed) fourier transform. A tool to do this is, is with a small script in numpy, gnu octave, or Matlab (or anything similar), but I currently have no time to dedicate to this.
* there are 2 interpretations of a wavetable: bandwidth limited or assuming all of its samples are converted by a pitch-synchronous DAC.
* there are many tricks to improve the properties of wavetable playback: https://www.dafx12.york.ac.uk/papers/dafx12_submission_69.pdf


#24

Cool, thanks @johannes :smile:

I am pretty sure that Serum uses windowed fourier transform. I know the opposite of what you mention. But if you import a file into Serum and export it again, it has got fade in/outs on each wave. This makes sure all indexing is pretty good and make sure that there is a lot less clicks that just importing a wav(.raw) into axoloti. That would create a lot more clicks. I made some wavetable from movie clips, with a lot going on, that works pretty well this way.

I kind of thought I covered most aspects of wavetables by now. But will defiantly look into the suggestions you have :smile:


#25

If you want to extract a single-period wavetable from a wave, window functions are useful.
However when re-processing a single-period wavetable into another single-period wavetable, a window function should not be used.


#27

I'm surprised with all the talk about how much work it is to convert found single cycle waveforms into proper wave tables that nobody has shared their results here.

Lots of talk, but no action? Or maybe people want others to have to do the frustrating work too?

In the mean time I've just found another waveform editor called AudioTerm

Look for the zip file in a link on that page.
If you can't find it - v2.27 is right here


#28

@adnauseam

I think some people in here are all ready sharing A LOT of stuff, like information, objects, subpatches, etc. And some people dont share anything, which is also okay...... But just because this is an open source platform and an open source community doesnt mean that you have to share everything you make.

If you want some wavetables there are many ways to get them;

  1. Make them yourself.
  2. Buy some.
  3. Find free ones.
  4. Ask others(kindly?).

There are many options. But like most things in life, putting an effort into what you want to accomplish does give great satisfaction :wink:

Waveterm... To me no good. I prefer to use Serum for editing and combining waves.


#29

@jaffasplaffa

In no way was I implying that I expect others to share with everyone or give us their finished wavetable, I was simply expressing my surprise.

I hadn't heard of Serum. Given that you own it's great that you can use it for your editing purposes. We can expect most won't want to pay almost $200 for wavetable software given the other free options.

I haven't tried AudioTerm yet but I understand it exports to WAV.

Cheers


#30

No worries :wink:

You dont have to pay anything, you can make them without Serum. It is just a lot more work. Allthough Serum is THET BEST. It is a VSTI which uses wavetables.... BUT the cool thinkg about serum is that you can drop 128 single waveforms into serum and it combines tthe waves into a wavetable of 128 waves. It adapts sizes and everything. Then you export the wavetable from Serum, convert it to raw and you are good to go :wink:

I am just sayin Serum is the easiest way to do it. But there are other ways which yuo can use for free.

That said, I know someone shared a smal bank with some allready converted waves in here somewhere... Did you find it?


#31

audioterm is pretty powerful, does more than the likes of serum, but is not the most obvious to use :wink:

Id actually recommend Tone2 Icarus over Serum for wavetable editing, its absolutely fantastic, allowing you to do things all sorts of crazy things... and can output individual waveforms or wavetables like serum.
(that said, i only used serum for a bit, so it whist i preferred Icarus, it may have some advantages that i didnt spot)


#32

Yeah it might do more....... But not as easy to use as serum. Actually I dont even think it does more. In Serum you can basically just drag in 128 waves and export as wav and you have got a wavetable. PERFECT fit in first shot... Audioterm doesnt do anything like that...

My recommendation goes to Serum.


#33

I came across this little wavetable generator in Python. Maybe someone here uses it already?

I quickly tried only some very basic functions, but it seems pretty okay. I'm more familiar with Python for maths (numpy, scipy), so it'll be nice to use those tools also to create wavetables. The module allows to set the sample size and rate. It can write to a .wav file, which is fine. But it would be nice to have it write to a headerless .raw straight away for use with Axoloti. Anyone who knows a Python function for that to be added?


#34

I am surprised to see no mention of libSox for audio conversion on Linux in this thread - it has utilities that can convert from wave to RAW, do sample rate conversion, etc. from the command line, so it can be scripted for batch processing. Full disclosure: I have not tried it myself for wave tables, although I probably will soon.

To the current point, it has python bindings: https://pypi.org/project/pysox/

Regards,
John


#35

Hi John, thanks. I use sox now to convert the files, which is already much quicker than putting them through Audacity. It would still be nice to write the output directly to .raw within the script. I'll check out pysox for that.

osc_gen is very easy to use. Here's a simple script to generate a wavetable that morphs between sine and triangle waves with 1024 samples per wave. This works fine with Axoloti.

from osc_gen import sig
from osc_gen import wavetable
from osc_gen import dsp

sg = sig.SigGen(num_points=1024)
wt = wavetable.WaveTable(16)
wt.waves = sig.morph((sg.sin(), sg.tri()),16)
wt.to_wav('osc_sin_tri.wav', samplerate=48000)

And then with sox:

sox osc_sin_tri.wav -r 48000 -c 1 osc_sin_tri.raw

#36

Nice find @joagogatao. I've written a Python script using osc_gen, pysoundfile, and pysox, that allows you to choose any two wave files, resizes them to any power of two that you wish, morphs between them over the desired number of wavetable slots (again, any power of two), and outputs a .raw wavetable. Here it is:

#wtgen.py, Jonathan Murphy 2020

from tkinter import Tk
from tkinter.filedialog import askopenfilename
from tkinter.filedialog import askdirectory
import soundfile as sf
import sox
from osc_gen import sig
from osc_gen import wavetable
from osc_gen import dsp

Tk().withdraw()

def is_power_of_two(n):
    return (n != 0) and (n & (n-1) == 0)

setup = input("1024 samples per wave, 64 slots per wavetable. Okay? y/n:")

if setup == "y":
    pts = 1024
    slts = 64
elif setup == "n":
    pts = int(input("How many samples per wave? Enter a power of two:"))
    if is_power_of_two(pts):
        print(pts, "Samples")
    else:
        print(pts, "Is not a power of 2!")
    
    slts = int(input("How many slots per wavetable? Enter a power of two:"))
    if is_power_of_two(slts):
        print(slts, "Slots")
    else:
        print(slts, "Is not a power of 2!")
else:
    print("Please enter y or n")
    
answer1 = input("Are you ready to select your first waveform? Enter y or n:")
if answer1 == "y":
    file1 = askopenfilename(filetypes=[("Wave Files", "*.wav")])
    f1 = sf.SoundFile(file1)
    #print('samples = {}'.format(len(f1)))
    if len(f1) == pts:
        print("File size matches!")
    else: 
        rat1 = len(f1) / pts
        #print('ratio = {}'.format(rat))
        tfm = sox.Transformer()
        tfm.speed(rat1)
        data, samplerate = sf.read(file1)
        new_data = tfm.build_array(input_array=data, sample_rate_in=f1.samplerate)
        #tfm.build_file(input_array=data, sample_rate_in=f1.samplerate, output_filepath=outputfilename)
        #tfm.build_file(file1, outputfilename)      
    #print('sample rate = {}'.format(f1.samplerate))
    #print('seconds = {}'.format(len(f1) / f1.samplerate))
elif answer1 == "n":
    print("That's a shame!")
    exit()
else:
    print("Please enter y or n")
    
answer2 = input("Are you ready to select your second waveform? Enter y or n:")
if answer2 == "y":
    file2 = askopenfilename(filetypes=[("Wave Files", "*.wav")])
    f2 = sf.SoundFile(file2)
    if len(f2) == pts:
        print("File size matches!")
    else: 
        rat2 = len(f2) / pts
        tfm2 = sox.Transformer()
        tfm2.speed(rat2)
        data2, samplerate2 = sf.read(file2)
        new_data_2 = tfm.build_array(input_array=data2, sample_rate_in=f1.samplerate)
elif answer2 == "n":
    print("That's a shame!")
    exit()
else:
    print("Please enter y or n")

answer3 = input("Are you ready to select your output directory? Enter y or n:")
if answer3 == "y":
    output_dir = askdirectory()
    print(output_dir)
    outputfilename = input("Enter a filename for your wavetable:")
    slash = "/"
    filetype = ".raw"
    outputfilename = output_dir + slash + outputfilename + filetype
    print(outputfilename)
    sg = sig.SigGen(num_points=pts)
    wt = wavetable.WaveTable(slts)
    wt.waves = sig.morph((sg.arb(new_data), sg.arb(new_data_2)), slts)
    sf.write(outputfilename, wt.waves, f1.samplerate, 'PCM_16')
elif answer3 == "n":
    print("That's really a shame!")
    exit()
else:
    print("Please enter y or n")