Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 9 of 9

Thread: Alternative sine-wave oscillator object

  1. #1
    Senior Member
    Join Date
    Jul 2020
    Posts
    398

    Alternative sine-wave oscillator object

    Following this paper: https://ccrma.stanford.edu/~jos/pdf/...AndSmith86.pdf

    I've created a sine wave generator that should perform as well as the AudioSynthWaveformSineHires object without
    nearly so much computation, basically 2 muls-rshifts and 2 add/subs per sample. Perhaps a candidate to replace the
    inefficient AudioSynthWaveformSineHires class?

    https://github.com/MarkTillotson/Aud..._resonator.cpp
    https://github.com/MarkTillotson/Aud...ed_resonator.h

    Seems to perform OK, I've made the frequency() setting function phase-continuous, and I think I've tweaked it so overflow
    never happens (some of these iterative methods can go slightly above the nominal amplitude at times at the extremes of
    frequency).

    I did a fair bit of modelling in Python for this and another (single multiply) iteration, and this performed better at low
    frequencies.

  2. #2
    Senior Member
    Join Date
    Jul 2020
    Posts
    398
    I've just found a paper that includes a summary of seven iterative sinusoid generators in an appendix:
    https://vicanek.de/articles/QuadOsc.pdf

    I guess I should investigate some more of them...

  3. #3
    Senior Member
    Join Date
    Apr 2020
    Location
    Tucson
    Posts
    127
    This is very cool. So you are versed in python and cpp? Your code is amazingly easy to read, good job.

    Can I ask: what might you be able to do with this? I'm trying to gain the cpp skills to build this for the teensy, it looks like you might have the skills to do it in an afternoon
    Attached Files Attached Files

  4. #4
    Senior Member
    Join Date
    Jul 2020
    Posts
    398
    Quote Originally Posted by boxxofrobots View Post
    This is very cool. So you are versed in python and cpp? Your code is amazingly easy to read, good job.
    Among others, yes - I try to make code clear so I can understand it down the line - visual formatting is everything for
    code clarity and for spotting bugs by eye - lots of whitespace always!
    Can I ask: what might you be able to do with this? I'm trying to gain the cpp skills to build this for the teensy, it looks like you might have the skills to do it in an afternoon
    Not sure what you mean - there's an image and a wav file there, not a user of JupyterNB myself.

  5. #5
    Senior Member
    Join Date
    Apr 2020
    Location
    Tucson
    Posts
    127
    OK, let's try this.
    Code:
    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib.image import imread
    import sounddevice as sd
    from scipy.signal import correlate
    import noise
    
    terrainsize = 256
    
    #load terrain from 256x256 or 512x512 png
    zvalues = imread('terrain256.png')
    
    #terrain is perlin noise
    #perlin noise generator coeffs
    
    freq=1400
    octaves=33
    
    for y in range(terrainsize):
                for x in range(terrainsize):
                    zvalues[x][y] = int(noise.snoise2(x / freq, y / freq, octaves) * 32767)
    
    sps = 44100                #sample rate
    duration_s = 1#0.0029            #duration of our waveform. .0029 at 44.1k makes 128 entry buffer. Set long as needed
    each_sample_number = np.arange(duration_s * sps)
    
    scale_x = terrainsize / 13.1  #this scales the orbit x size
    scale_y = terrainsize / 8.1  #scale orbit y size. Changing these vales changes orbit from circular to ellipse
    
    offset_x = 140                 #center of orbit offset x terrain axis
    offset_y = 120                 #center of orbit offset y terrain axis
    
    freq_hz = 108.0            #osc freq    
    fm=1.00
                                  # for this demo, moving these values will aptly demonstrate the wave terrain.
    def wavetable(_size): ##122uS per loop on my machine to make geometry calculations on 2900uS buffer frame of audio
        wavex = np.sin(2 * np.pi * each_sample_number * freq_hz * fm / sps)
        wavey = np.cos(2 * np.pi * each_sample_number * freq_hz * fm / sps)
        tablex = np.int16(offset_x + (wavex * scale_x))
        tabley = np.int16(offset_y + (wavey * scale_y))
        ctablex = np.clip(tablex,0,terrainsize-1)  #clipping to terrainsize takes as long as making the tables
        ctabley = np.clip(tabley,0,terrainsize-1)  
        ctablez = [x for x in range(_size)]
        for v in range(0,_size):
            ctablez[v] = zvalues[ctablex[v],ctabley[v]]
    
        ctablez = np.int16(ctablez)    
        return ctablez
    That's code for a wave terrain oscillator. The wav file is the result of playing back a Perlin noise terrain. It uses two oscillators in quadrature to generate an x,y coordinate to read magnitude from a table. This version uses a 256x256 table for 16 bit use. but a terrain oscillator with 65536 x 65536, tracking a plane of coordinates from -(2,147,483,648) to + (2,147,483,647) would give you very good lfo accuracy. Of course, the great thing about it is it makes an excellent oscillator in general. Maybe I'll try to hack your code. I just need to be able to read a 256x256 array, not as easy in c as in python. Nothing is as easy in c as it is in python

  6. #6
    Senior Member
    Join Date
    Jul 2020
    Posts
    398
    Quote Originally Posted by boxxofrobots View Post
    OK, let's try this.
    ...

    That's code for a wave terrain oscillator. The wav file is the result of playing back a Perlin noise terrain. It uses two oscillators in quadrature to generate an x,y coordinate to read magnitude from a table. This version uses a 256x256 table for 16 bit use. but a terrain oscillator with 65536 x 65536, tracking a plane of coordinates from -(2,147,483,648) to + (2,147,483,647) would give you very good lfo accuracy. Of course, the great thing about it is it makes an excellent oscillator in general. Maybe I'll try to hack your code. I just need to be able to read a 256x256 array, not as easy in c as in python. Nothing is as easy in c as it is in python
    Had a quick play with this - it needs interpolation between samples to prevent aliasing for low frequencies I think
    - it had the jaggies at 108Hz due to the small orbit size. In other words the map should be bilinearly interpolated
    or similar (you'd get that for free in a renderer implementation!). C arrays are just as easy to read as Python surely
    - so easy you can read off the end of them even!

    I was actually thinking of doing an explicit quadrature oscillator object for the Audio lib as it happens. I found another good
    paper on recursive oscillators too, http://www.claysturner.com/dsp/2nd_OSC_paper.pdf

  7. #7
    Senior Member
    Join Date
    Jul 2020
    Posts
    398
    Quote Originally Posted by MarkT View Post
    I was actually thinking of doing an explicit quadrature oscillator object for the Audio lib as it happens.
    And here it is!

    https://github.com/MarkTillotson/Aud...h_quadrature.h

    https://github.com/MarkTillotson/Aud...quadrature.cpp

  8. #8
    Senior Member
    Join Date
    Apr 2020
    Location
    Tucson
    Posts
    127
    That will be perfect for building an am radio transceiver in the audio object. Very cool, thanks.

    This reminds me: we need a half wave detector as well. The full wave rectifier is not suited as a detector.

  9. #9
    Senior Member
    Join Date
    Jul 2020
    Posts
    398
    Quote Originally Posted by MarkT View Post
    And here it is!
    And I've been looking at the output on my spectrum analyzer today, couldn't see any issues and the main peak has
    perhaps a trace of phase noise close-in:
    Click image for larger version. 

Name:	quadrature_oscillator_10k.png 
Views:	10 
Size:	10.8 KB 
ID:	21617

    The frequency span is 200Hz, tone at 10kHz

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •