Page 1 of 1
Sound generator module
Posted: Sat Feb 11, 2023 9:35 pm
by Bigfoot71
Hi all people
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
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
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!
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!
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