[SOLVED] Changing frequency while synthesising sine wave

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
busboy
Prole
Posts: 3
Joined: Mon Apr 19, 2021 7:19 am

[SOLVED] Changing frequency while synthesising sine wave

Post by busboy »

Hello,

I've been stuck for days on changing the frequency of a sine wave oscillator over a period of time, and I'm completely stumped.

Image

I've tried it many number of ways, but what always ends up happening is that the frequency over the lerp of its duration is never correct, it always either overshoots by the end, or goes too low and comes back up. You can see this very easily by entering hz values that are really high or really low in the frequency table.

I've tested every part of the program for the cause of the error, I've tried other sine oscillators (I'm currently using one that was shared by zorg in https://love2d.org/forums/viewtopic.php?t=82944), I've tried replacing variables with constants every step of the way, I've tried pretty much everything.

I've put together a some code in love.draw() to better illustrate what's going on in the wave form, and you can press space at any time to hear the sound.

As you can see in the frequency table, the sound should first descend from 130 to 65 hz over a second, then hold on 65hz for a second, then ascend again to 130 over the final second. But the middle second is the only one that works correctly, for some reason, everything else acts totally erratically and I can't understand why.

I've included my main.lua as code below, and my love game as an attachment. Thank you!

Code: Select all

sound = {}

sound.rate = 44100  --sample rate
sound.bits = 16     --bit rate
sound.channel = 1
sound.initialPhase = 0

function lerp(a, b, t) return a + (b - a) * t end -- linear interpolation

function love.load()

    love.window.setMode(1200, 200, { vsync = true, highdpi = true, resizable = true })

    --frequency table
    frequency = {130.81278265029931, 65.406391325149656, 65.406391325149656, 130.81278265029931}
    --time table
    seconds = {0, 1, 2, 3}

    --tone is the sound data, sound_table is the x points of the waveform
    tone, sound_table = sound.get()

    qs = love.audio.newQueueableSource(tone:getSampleRate(), tone:getBitDepth(), tone:getChannelCount())
end

function love.keypressed(key)--, scancode, isrepeat)

    if key == "space" then
        qs:queue(tone)
        qs:play()
    end
end


function love.draw()
    -- draw waveform
    if sound_table then -- draw a line dividing each second
        love.graphics.setColor(255, 255, 255, 0.2)
        for i=1, #seconds do
            love.graphics.line(300*seconds[i], 0, 300*seconds[i], love.graphics.getHeight())
        end

        love.graphics.setColor(255, 255, 255, 1) --draw the waveform as dots
        for i=1, #sound_table-1 do
            love.graphics.points(100*seconds[1] + (100*(i-1))/sound.rate*3, (100*sound_table[i])+(100))
        end
    end

end

-- Constructor for a sine wave generator.
sine = function(generator)
    local tau = math.pi*2
    local generator = generator
    local increment = 1.0 / generator.rate --/ generator.channels
    local phase = generator.initialPhase
    return function(freq)
        phase = phase + increment
        generator.phase = phase
        local x = phase * freq
        -- 2 ops: 1 mul, 1 trig
        return math.sin(tau * x)
    end
end

function sound.get()

    local sound_table = {}
    
    local length = (seconds[#seconds]-seconds[1]) * sound.rate

    -- initialising sample
    local sound_data = love.sound.newSoundData(length, sound.rate, sound.bits, sound.channel)

    local oscillator = sine(sound)

    -- writing to sample
    local amplitude = 0.5

    for i = 1, #seconds-1 do
        
        from = (seconds[i]-seconds[1]) * sound.rate
        till = (seconds[i+1]-seconds[1]) * sound.rate - 1
        from, till = math.floor(from), math.floor(till) --rounding down the samples, just in case

        for s = from, till do

            now = lerp(frequency[i], frequency[i+1], (s-from)/(till-from))

            sample = oscillator(now) * amplitude
            sound_data:setSample(s, sample)
            table.insert(sound_table, sample)
        end
    end

    return sound_data, sound_table

end

--manual, deconstructed loop below
--[[
function sound.get(args, ...)

    local sound_table = {}
    
    local length = 3 * sound.rate

    -- creating an empty sample
    local sound_data = love.sound.newSoundData(length, sound.rate, sound.bits, sound.channel)

    local oscillator = sine(sound)

    s = 0

    -- filling the sample with values
    local amplitude = 0.5

        for i = 0, 44100 do

            s = i
            now = lerp(frequency[1], frequency[2], i/44100)
            
            --if i == 2 and s == from and (s-from)/(till-from) == 0 then debug1 = now end
            --if i == 1 and s == till and (s-from)/(till-from) == 1 then debug2 = now end

            sample = oscillator(now) * amplitude
            sound_data:setSample(s, sample)
            table.insert(sound_table, sample)
        end

        for i = 44100, 88200 do

            s = i

            now = lerp(frequency[2], frequency[3], (i-44100)/(88200-44100))
            
            --if i == 2 and s == from and (s-from)/(till-from) == 0 then debug1 = now end
            --if i == 1 and s == till and (s-from)/(till-from) == 1 then debug2 = now end

            sample = oscillator(now) * amplitude
            sound_data:setSample(s, sample)
            table.insert(sound_table, sample)
        end

        for i = 88200, 132300-1 do

            s = i

            now = lerp(frequency[3], frequency[4], (i - 88200)/(132300-88200))
            
            --if i == 2 and s == from and (s-from)/(till-from) == 0 then debug1 = now end
            --if i == 1 and s == till and (s-from)/(till-from) == 1 then debug2 = now end

            sample = oscillator(now) * amplitude
            sound_data:setSample(s, sample)
            table.insert(sound_table, sample)
        end

    return sound_data, sound_table

end]]
Attachments
sine.love.zip
(2.13 KiB) Downloaded 256 times
Last edited by busboy on Tue Apr 20, 2021 2:53 pm, edited 1 time in total.
User avatar
zorg
Party member
Posts: 3465
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Changing frequency while synthesising sine wave

Post by zorg »

Haven't had time yet to look at it too much, but one thing you might try is to limit the phase in the returned generator function to the range/domain (i get these mixed up) of [0,1] with something like:

Code: Select all

phase = (phase + increment) % 1.0
...that said, i haven't noticed you dividing x by the sample rate either, that should happen if you want the correct frequencies to play.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
busboy
Prole
Posts: 3
Joined: Mon Apr 19, 2021 7:19 am

Re: Changing frequency while synthesising sine wave

Post by busboy »

Thanks for your reply!
zorg wrote: Tue Apr 20, 2021 3:47 am Haven't had time yet to look at it too much, but one thing you might try is to limit the phase in the returned generator function to the range/domain (i get these mixed up) of [0,1] with something like:

Code: Select all

phase = (phase + increment) % 1.0
Unfortunately all this seemed to do is affect the continuity of the signal from one frequency point to another. Your sine oscillator not having one of these limits was also what kept it from clicking like all the others I tried.

without phase limit:
Image

with phase limit:
Image
zorg wrote: Tue Apr 20, 2021 3:47 am ...that said, i haven't noticed you dividing x by the sample rate either, that should happen if you want the correct frequencies to play.
Would that just be replacing the x declaration in the returned function with

Code: Select all

local x = (phase * freq)/generator.rate
Because that just flattens the line.
Image

I'm not sure I fully understand, I apologise.
User avatar
zorg
Party member
Posts: 3465
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Changing frequency while synthesising sine wave

Post by zorg »

>Would that just be replacing the x declaration in the returned function with <code>
Yes, it would... i feel like something else is amiss, but again, i'd need to dwelve into it, and due to me working today, that's not gonna happen.
I did notice that you did multiply with sound.rate in your code, so that might be why it could have worked before... i just feel that that's in the wrong place.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
pgimeno
Party member
Posts: 3656
Joined: Sun Oct 18, 2015 2:58 pm

Re: Changing frequency while synthesising sine wave

Post by pgimeno »

This seems to work:

Code: Select all

    return function(freq)
        phase = phase + tau * increment * freq
        generator.phase = phase
        return math.sin(phase)
    end
Why is it different, I haven't stopped to analyse yet. By the way, you can save a multiplication by pre-calculating tau * increment.

Edit: Intuitively, the phase shift should be affected by frequency, and in your original formulation you don't have that; the phase is always the same for a given instant in time, which isn't right. Your original formulation works for a fixed frequency, but not for an arbitrarily changing frequency.
busboy
Prole
Posts: 3
Joined: Mon Apr 19, 2021 7:19 am

Re: Changing frequency while synthesising sine wave

Post by busboy »

pgimeno wrote: Tue Apr 20, 2021 10:56 am

Code: Select all

    return function(freq)
        phase = phase + tau * increment * freq
        generator.phase = phase
        return math.sin(phase)
    end
This is dead on, thank you so much.
User avatar
darkfrei
Party member
Posts: 1197
Joined: Sat Feb 08, 2020 11:09 pm

Re: [SOLVED] Changing frequency while synthesising sine wave

Post by darkfrei »

How it works?
Attachments
2021-04-20T17_38_49-Untitled.png
2021-04-20T17_38_49-Untitled.png (64.99 KiB) Viewed 6990 times
sounds-01.love
(2.31 KiB) Downloaded 441 times
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
User avatar
zorg
Party member
Posts: 3465
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: [SOLVED] Changing frequency while synthesising sine wave

Post by zorg »

I checked the post of mine from the thread you linked, and i got it wrong there as well... guess i'll take the blame fair and square. :D
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
Post Reply

Who is online

Users browsing this forum: Bing [Bot] and 15 guests