Playing with wavetables
  • I'm playing with an idea but am running into limitations in my ability.

    Since I've been able to generate Audulus patches with code, I figured I would wire up some mux nodes and phase through them very fast to make a wavetable of sorts. I've attached a first pass that I generated from a wave file (AdventureKid's single cycle waveforms): https://www.adventurekid.se/akrt/waveforms/adventure-kid-waveforms/

    However, it's aliasing a bunch. There are a number of places where I could be going wrong:
    - not understanding how to filter the signal coming off a wave table like this
    - not actually parsing the wave file correctly
    - not implementing my mux64 nodes correctly

    Anyway, I'm going to keep tinkering with it.
    wavetable.audulus
    911K
  • I've thought about this too. The aliasing is probably due to the bajillion switches and how Audulus doesn't always scan inputs at audio rates. That said, the patch sounds good and I'm really curious how you are able to generate audulus patches code. Did you backwards engineer the code or something?
  • RobertSyrett I've been building up some code here: https://github.com/jjthrash/conway-audulus

    The key pieces for the wavetable part are audulus.rb and build_wave_table_node.rb (both written in Ruby).

    I did reverse engineer the Audulus patch format, which as of Audulus 3 is straightforward and pretty easy to work with.
  • This is massively insane and I'm soooo glad you wrote code to write it for you lol I thought at first you did each one of these samples by hand!

    I told Taylor about this hopefully he'll check it out tomorrow - would love to see what he thinks of the git stuff you linked to - a little beyond me, but it might spark some ideas for him! :)
  • And yeah it's aliasing like a mofo but it also sounds awesome.
  • This looks promising: http://hackmeopen.com/2010/11/bandlimited-wavetable-synthesis/

    Specifically: Gibbs Phenomenon

    There’s one last thing. When generating signals with fast transitions, we have to apply a little bit of filtering during the construction of the wavetable. The Gibbs Phenomenon is caused by the sharp cutoff at the Nyquist Rate of the summation of the partial factors, the individual frequency components, of the wavetable we are building. The fact that we stop abruptly at the Nyquist Rate causes ripples in the pass band. This is noticeable when you build a wavetable that you’ll see ripples in the wavetable near sharp transitions. It is very audible in the signal. So, each sample must be multiplied by a factor to low-pass filter the array, reducing the amplitude of the higher partials as the summation approaches the Nyquist Rate. Each sample should be multiplied by:

    m = cos^2((current_partial)-1)*(pi/(2*total_number_of_partials)
  • I've played with a few things to get rid of the aliasing, but so far nothing has worked. My understanding has increased, though.. Gibbs phenomenon really has nothing to do with it, or at least a lot less than I thought.

    I've tried by downsampling the WAV to a power of 2 (512 samples), then skipping samples as I get to higher frequencies so that I'm not cycling through the samples at greater than the system sample rate, but it doesn't really make any difference.

    Giving up on it for now. Was a neat idea. :)
  • @taylor have anything to add here if you're not too busy? Insight on why it's not working?
  • Just for fun, I modified the code to build a spline node on the same principles. It also aliases like crazy, but at least the cpu use is now only 2%...
    wavetable.audulus 2017-11-04 12-11-06.png
    1024 x 790 - 103K
  • I find that if you run an aliasing waveform through the LFP node it sounds alright!
  • @RobertSyrett I do run the waveform through an LPF at 2xHz and it doesn't appear to make any difference. I suppose I could run it at Hz, but the aliases don't sound like they are happening a very high pitch. This brings me to just how ignorant I am of basic DSP.

    My next attempt is roughly based on the article I linked to earlier. Because I believe part of the problem is getting partials that are past the nyquist limit as the pitch goes up, I have built 8 waveforms that have steadily dampened partials, and a mux node that selects between them automatically based on the input pitch.

    I *think* the results are better, but there is still aliasing. I've attached a sample patch that uses a theremin wave table and sounds pretty decent.

    I've pushed the code I'm using for it up to here (I really need to rename the project): https://github.com/jjthrash/conway-audulus

    The fourier analysis part of the code is here: https://github.com/jjthrash/conway-audulus/blob/master/build_wave_table_node.rb#L234

    I'm happy to explain my rationale further if anybody is interested.
    testbed.audulus
    141K
  • Ok, right after I posted I realized the glaring error in my code. I'm going to try to fix it and report back whether it fixed anything about the sound.
  • I think I figured it out! I've attached a patch that actually sounds pretty decent. This one isn't the Theremin patch.

    I would love it if you could play with it and let me know what you think. My next step would be to design it to fit into the current ecosystem, and after that to build patches for all the Adventure Kid waveforms.
    testbed.audulus
    141K
  • Nery nice! I will have to get it up on my oscilloscope to see how the wavetable translates. Also I'm curious is there a reason why you couldn't crossfade the waveforms for smooth smoothness?
  • @RobertSyrett Thanks!

    I have imagined a scheme for a cross-fading mux node (engage cross fader when close to a cutover point, 100% individual wave table at other points) that would do just that, but this works for a prototype for now. To be honest, I haven't actually listened that closely to the patch.. just enough to tell if the aliasing is gone.

    Also, another improvement I might make is to scale my partial dampening logarithmically. My hope is it would still address the aliasing, but leave more partials intact for richer sound.
  • I have removed the LPF, which was removing too much. After doing that, the edges between wavetables was obvious. So,

    I implemented a cross-fading mux8 node. It may be a bit overengineered.. hopefully I can figure out a simpler way to manage the crossfading.
    testbed.audulus
    178K
  • By the way, when fiddling with math to accomplish various things, I've really enjoyed Desmos. For the cross-fade transition, I used: https://www.desmos.com/calculator/eklcaacz6v
  • OK, discovered smoothstep and migrated to using it instead of my crazy arctangent function for the cross fading mux node.
    testbed.audulus
    178K
    xmux.audulus
    18K
  • @jjthrash I guess you figured out that the docs are incorrect. It's smoothstep(a,b,x). I used for a x-fader on the linear zero-crossing FM oscillator I posted a while back. It comes in handy at times.
  • @stschoen That's right. I also submitted a pull request to fix them. :)
  • @stschoen By the way, I figured out the parameters were mixed up after looking at your zero-crossing FM oscillator.
  • I have often wondered how smoothstep work, as I find the description cryptic:
    smooth step from 0 to 1 on the interval [a, b]
  • @RobertSyrett It is apparently a common function used in animation.. an applied sigmoid for just this purpose: https://en.wikipedia.org/wiki/Smoothstep

    I've seen it called "ease in/out"
  • Oh, for further explanation, it works like this:

    if x < a, it outputs 0
    if x > b, it outputs 1
    if a < x < b, it smoothly moves between 0 and 1 with a curve like this:
    Smoothstep and Smootherstep - Smoothstep - Wikipedia 2017-11-07 17-15-16.png
    1358 x 1044 - 145K
  • In my case, I used it to drive the cross faders on my cross faded mux node. As the "sel" value approached the next step, it smoothly crossfades into the next step.
  • How does it determine the rate at which it interpolates the step though? It seems like a slew but I must be missing something. I'll read the wiki article.
  • Just for fun I built patches for all 4000+ of the AKWF waveforms: https://jimmy-share.s3.amazonaws.com/AKWF_audulus_20171107.zip

    The UI isn't final.. the variability of the text width leads to some wonky looking layouts, since these are all auto-generated.

    I changed them to be Octave-input instead of Hz input. No Sync input.

    Now I need to play with them.. There are 4357.. not sure where to start.
  • I believe there's a problem with my WAV file reading. I coded it myself, so I shouldn't be surprised. Probably best to ignore the big patch dump above. :P Will try to fix it tomorrow.
  • I've fixed the problem with the WAV file reading, and uploaded all the patches again: https://jimmy-share.s3.amazonaws.com/AKWF_audulus_20171108.zip

    Time to start playing with them... Adventure Kid also has a set of NES sounds that I will probably patchify.
  • Here's a low effort patch to make a bass guitar sound.
    bass_guitar.audulus
    420K
  • Here are the NES sounds: https://jimmy-share.s3.amazonaws.com/AKWF_nes_audulus_20171108.zip

    The triangle sounds pretty convincing...
  • Here's a quick guide to the waveforms, though I don't have them organized in the folders that he does: https://www.adventurekid.se/akrt/waveforms/adventure-kid-waveforms/individual-folder-downloads-of-the-akwf-pack/
  • This is awesome. Thanks man!