Page 1 of 1

Sound generator module

Posted: Sat Feb 11, 2023 9:35 pm
by Bigfoot71
Hi all people :D

I'm just sharing with you a little script I made for a game I'm working on and which could help some I think, it's a Lua module to generate sounds with 6 different types of waves and with some options in plus the possibility of making small melodies with another function ^^

Here are the possible types of waves:
  • sine
    square
    triangle
    sawtooth
    pulser
    composite
I started from the model in the wiki which is really useful then a lot of lessons from right to left, I made a demo having tried to reproduce the Mario theme, I don't know if it's a success you will tell me if there are real musicians :awesome:

If you have any suggestions for optimizations or additions I am all ears I even made a repository if necessary ^^

I forgot to specify that I added a table with all the note frequencies from C1 to G9 :crazy:

Links that have been useful to me:
https://thewolfsound.com/sine-saw-squar ... synthesis/
http://www.mathguide.com/lessons2/EquationsNotes.html
http://techlib.com/reference/musical_no ... encies.htm
https://pages.mtu.edu/~suits/NoteFreqCalcs.html
https://pages.mtu.edu/~suits/notefreqs.html

Re: Sound generator module

Posted: Sun Feb 12, 2023 6:38 am
by Andlac028
Bigfoot71 wrote: Sat Feb 11, 2023 9:35 pm If you have any suggestions for optimizations or additions I am all ears I even made a repository if necessary ^^
I just looked at the code and there are some suggestions for optimizations:

Use local variables also for functions like math.floor, if they are used very often in loop, because it will eliminate table lookups:

Code: Select all

local floor = math.floor
for … do
    result = floor(…)
end
In genMusic, you call genNote and then concat all the soundDatas to new soundData. That created unnecesarry soundDatas and invokes copying all the data. Try to compute length of soundsData for genMusic and only pass soundData with offsets to genNote, so there is no unnecesarry soundData for every note.

Edit: fixed some typos

Re: Sound generator module

Posted: Sun Feb 12, 2023 12:50 pm
by Bigfoot71
Andlac028 wrote: Sun Feb 12, 2023 6:38 am I just looked at the code and there are some suggestuins for optimizations:

Use local variables also for functions like math.floor, if they are used very often in loop, because it will eliminate table lookups:

Code: Select all

local floor = math.floor
for … do
    result = floor(…)
end
In genMusic, you call genNote and then concat all the soundDatas to new soundData. That created unnecesarry siundDatas and invikves copying all the data. Try to comoute length of soundsData for genMusic and only pass soundData with offsets to genNote, so there is no unnecesarry soundData.
Thank you for these suggestions! I feel a little stupid because for math.floor (and other math functions) it was so obvious that I could have asked myself the question before sharing but it's done thank you!

I also made a real demo where you can try out all the waveforms for the sounds.

Re: Sound generator module

Posted: Mon Feb 13, 2023 12:24 am
by zorg
Not many things i can nitpick about with reason tbh, so good job; I guess just if you'd want to write/have less lines of code, you could generate the frequencies of the notes with an algorithm instead of hardcoding the values.

Code: Select all

notes = {}

local A4 = 440.00
for n = 12, 131 do -- From C0 to B9
  table.insert(notes, A4 * 2^((n-69)/12))
end
I would also personally simplify the generators, since the p period thing imo is not that important... but i guess it does make sense if you're just generating specific length sounds... i wouldn't bother with that for a more complete synth that could realtime generate sound since it would be meaningless.

Re: Sound generator module

Posted: Mon Feb 13, 2023 10:15 am
by Bigfoot71
zorg wrote: Mon Feb 13, 2023 12:24 am Not many things i can nitpick about with reason tbh, so good job; I guess just if you'd want to write/have less lines of code, you could generate the frequencies of the notes with an algorithm instead of hardcoding the values.

Code: Select all

notes = {}

local A4 = 440.00
for n = 12, 131 do -- From C0 to B9
  table.insert(notes, A4 * 2^((n-69)/12))
end
I would also personally simplify the generators, since the p period thing imo is not that important... but i guess it does make sense if you're just generating specific length sounds... i wouldn't bother with that for a more complete synth that could realtime generate sound since it would be meaningless.
I'm not hugely savvy in music and I wouldn't even have dared to imagine that such a simple algorithm could generate "all" musical notes, thank you very much, I'll see how to integrate this to keep the letters of the notes in keys of values ​​for the table! :awesome:

Edit: Mission accomplished! Here's how I did:

Code: Select all

local notes = {}; do

    local A4 = 440.00
    local note_names = {
        "C", "C#", "D",
        "D#", "E", "F",
        "F#", "G", "G#",
        "A", "A#", "B"
    }

    for n = 12, 131 do -- From C0 to B9
        local note_frequency = A4 * 2^((n-69)/12)
        local octave = math.floor(n/12)-1
        local note_index = n % 12 + 1
        local note_name = note_names[note_index]..octave
        notes[note_name] = note_frequency
    end

end
Thanks again for this suggestion! :nyu:

Otherwise I can't find any way in pure Lua with only Löve2d to produce sounds in real time, OpenAL seems to allow it so does Löve2d also allow it "natively"?

Re: Sound generator module

Posted: Tue Feb 14, 2023 7:10 am
by zorg
Bigfoot71 wrote: Mon Feb 13, 2023 10:15 am Otherwise I can't find any way in pure Lua with only Löve2d to produce sounds in real time, OpenAL seems to allow it so does Löve2d also allow it "natively"?
Yes, you're looking for love.audio.newQueueableSource and love.sound.newSoundData primarily.

Code: Select all

local tau = 2.0 * math.pi
local buffer = love.sound.newSoundData(2048,44100,16,1) -- 2048 is how many samplepoints each channel has
local qsource = love.audio.newQueueableSource(44100,16,1,2) -- 2 is how many OpenAL-internal buffers the source has

-- in update
if qsource:getFreeBufferCount() > 0 then
  -- generate one buffer's worth of audio data; the above line is enough for timing purposes
  local phase = 0.0
  for i = 0, buffer:getSampleCount()-1 do
    local smp = math.sin(tau * phase)
    for c = 1, buffer:getChannelCount() do
      buffer:setSample(i, c, smp)
    end
    phase = (phase + (440.00 / buffer:getSampleRate())) % 1.0
  end
  -- queue it up
  qsource:queue(buffer)
end
qsource:play() -- keep playing so playback never stalls, even if there are underruns; no, this isn't heavy on processing.

Note that with vsync on, the buffer size won't be enough since you'll need x/samplerate > 1/refresh_rate samplepoints to be queued each time update is called.

Re: Sound generator module

Posted: Tue Feb 14, 2023 10:49 pm
by Bigfoot71
zorg wrote: Tue Feb 14, 2023 7:10 am Yes, you're looking for love.audio.newQueueableSource and love.sound.newSoundData primarily.

Note that with vsync on, the buffer size won't be enough since you'll need x/samplerate > 1/refresh_rate samplepoints to be queued each time update is called.
Thank you so much ! I'll see what I can do cool with all that :awesome: