Thread Rating:
  • 1 Vote(s) - 4 Average
  • 1
  • 2
  • 3
  • 4
  • 5
SFX
#1
Marcus,

I am curious as to "how" your SFX program actually generates tones. For instance: Most Music Notation software relies on a "soundfont" (database of stored samples) to play various instruments but usually require the use of a midi compatible sound card...  Are you using a 'secret' method (similar to KFC's herbs and spices) to access the midi port?... Perhaps a little magic or 'slight of hand'? Just curious...

J
Logic is the beginning of wisdom.
Reply
#2
(02-19-2024, 07:02 PM)johnno56 Wrote: Marcus,

I am curious as to "how" your SFX program actually generates tones. For instance: Most Music Notation software relies on a "soundfont" (database of stored samples) to play various instruments but usually require the use of a midi compatible sound card...  Are you using a 'secret' method (similar to KFC's herbs and spices) to access the midi port?... Perhaps a little magic or 'slight of hand'? Just curious...

J

I'm not sure at which level I should answer this question.

N7 uses a rather low level sound api named PortAudio, which is cross platform and open source. It's a very simple library. I (as in every running n7 program) constantly feed PortAudio with a stream of bytes, which ends up as sound in the speakers. So when no sound is playing, I just send a constant stream of zeroes to PortAudio. If one or more sound effects and maybe some music are playing, I manually mix (simple addition) those sound sources and feed PortAudio with the resulting data.

A sound effect is just some data that varies over time. The sample rate decides how many data points a sound is made up from per second. So, a sound effect that lasts for 1 second at a sample rate of 16000 requires exactly 16000 data points. Those data points are what the SFX library creates. The 'createsound' function, that SFX uses, just creates a sound effect in memory from the data and sample rate provided. There's not much difference  between doing that and loading a WAV file with 'loadsound' (which creates a sound effect in memory from the content of a WAV file).

Creating sound data (like SFX does) isn't very difficult. To create a simple sine wave sound (BEEEEEEEEEEEEP) of a certain frequency, you just calculate how much the angle (sen't to 'sin') should change per data point based on the sample rate. Then you fill an array with those sin values. To create cool effects, I also let the frequency vary over time (the 'freq' array that you send to SFX.SineWave). Add some variable volume to that (the 'vol' array parameter) and all sorts of fun things start to happen. The echo effect is just a matter of copying, modifying and pasting the original sine wave effect to a new array (well, not quite that simple).
Reply
#3
I walked to the store and back and did some more thinking on the subject.

You could probably never use the SFX library to create, let's say, a piano tone. Because a piano tone contains lots of overtones and subtones. That is, a piano tone isn't a single sine wave. You would need to add several layers of sine waves with double (and double double, can't find the word, and so on) the frequency, and half (and half half ...) the frequency, all of these at much lower volume. And then there's the thing called "attack, sustain, release and decay", but that you could probably control with the 'vol' array.

Hm, maybe you could just call SFX.SineWaveData to get data for the main tone and all the over- and subtones and add them together into a new array and then send that array to 'createsound'.
   I think I've got to try this, to create the sound of a piano, when I get the time Big Grin

Sorry for rambling.
Reply
#4
Rambling? Nah! I am a pro at rambling... lol

Portaudio, you say? Cool. Do not panic... I am not expecting piano tones... That would be silly... Even Music Notation software does not 'create' a piano tone... that is why they use pre-recorded samples... What I am expecting is a Synthesizer... Moo Ha Ha Ha...

Hang on... "walked to the store and back"... Correct me if I am wrong... It 'is' Winter where you are, is it not? lol
Logic is the beginning of wisdom.
Reply
#5
Here's an example where I create a plain sound and the same sound but with some over- and subtones. Sounds like some sort of electric piano Big Grin  But maybe SFX could get another parameter dealing with these things.

Code:
include "sfx.n7"

#mem32000000
sfx = SFX()

set window "Test", 640, 480

duration = 0.75
freq = 523*0.5
vol = [0.75, 0.2, 0.2, 0.2, 0]
main = sfx.SineWaveData(duration, freq, vol)[0]
plainSnd = createsound(main, main, sfx.GetSampleRate())
hiFreq = freq
loFreq = freq
for i = 1 to 4
    hiFreq = hiFreq*2
    loFreq = loFreq/2
    for j = 0 to sizeof(vol) - 1  vol[j] = vol[j]*0.2
    high = sfx.SineWaveData(duration, hiFreq, vol)
    low = sfx.SineWaveData(duration, loFreq, vol)
    for j = 0 to sizeof(main) - 1
        main[j] = main[j] + high[0][j]
        main[j] = main[j] + low[0][j]
    next
next
newSnd = createsound(main, main, sfx.GetSampleRate())
wln "press 1 to play the plain sound"
wln "press 2 to play the new sound"
while not keydown(KEY_ESCAPE, true)
    if keydown(KEY_1, true)  play sound plainSnd
    if keydown(KEY_2, true)  play sound newSnd
    fwait 60
wend
Reply
#6
(02-20-2024, 06:52 PM)johnno56 Wrote: ... I am not expecting piano tones...
.. What I am expecting is a Synthesizer... 


Just curious with "do re mi" generated by SquareWave function, 
let's try this Pianoman ..... Cool

Code:
'----------------
' INITIALIZATION
'----------------
include "sfx.n7"
set window "Pianoman", 160, 200,false,3

constant DO = 261.63*2
constant RE = 293.66*2
constant MI = 329.63*2
constant FA = 349.23*2
constant SO = 392.00*2
constant LA = 440.00*2
constant TI = 493.88*2
constant b1 = 0.2
constant b2 = 0.4

visible sfx = SFX() 'create an sfx instance

'--------------
' MAIN PROGRAM
'--------------
wln "PIANOMAN"
wln
wln "1 - Play DO"
wln "2 - Play RE"
wln "3 - Play MI"
wln "4 - Play FA"
wln "5 - Play SO"
wln "6 - Play LA"
wln "7 - Play TI"
wln "8 - Play Song"
wln
wln "Esc - Quit"

while not keydown(KEY_ESCAPE)
    if keydown(KEY_1, true) then Piano(b1,DO)
    if keydown(KEY_2, true) then Piano(b1,RE)
    if keydown(KEY_3, true) then Piano(b1,MI)
    if keydown(KEY_4, true) then Piano(b1,FA)
    if keydown(KEY_5, true) then Piano(b1,SO)
    if keydown(KEY_6, true) then Piano(b1,LA)
    if keydown(KEY_7, true) then Piano(b1,TI)
    if keydown(KEY_8, true) then Song()
    wait 1
wend

'-----------
' FUNCTIONS
'-----------
function Piano(b,x)
    'SquareWave(duration,freq,volume)
    mytone = sfx.SquareWave(b,x,0.05)
    play sound mytone
    Pause()
endfunc

function Song()
    Piano(b2,SO);Piano(b1,SO);Piano(b1,SO)
    Piano(b2,SO);Piano(b2,MI)
    Piano(b2,DO*2);Piano(b1,DO*2);Piano(b1,DO*2)
    Piano(b2,TI);Piano(b2,LA)
   
    Piano(b2,SO);Piano(b1,SO);Piano(b1,SO)
    Piano(b2,LA);Piano(b1,SO);Piano(b1,SO)
    Piano(b2,SO); Piano(b1,FA); Piano(b1,MI)
    Piano(b2,RE); Piano(b1,DO); Piano(b1,RE)
   
    Piano(b2,MI); Piano(b1,MI); Piano(b1,MI)
    Piano(b2,RE); Piano(b2,RE)
    Piano(b1,DO); Piano(b1,RE); Piano(b1,MI);Piano(b1,DO)
    Piano(b1,LA/2);Piano(b1,TI/2);Piano(b2,DO)
   
    Piano(b2,SO/2);Piano(b1,DO); Piano(b1,RE)
    Piano(b2,SO); Piano(b1,FA); Piano(b1,MI)
    Piano(b2,RE); Piano(b1,RE); Piano(b1,RE); Piano(b2*2,DO)                                                   
endfunc

function Pause()
    wait 400
endfunc
Reply
#7
Interesting... that tune sounds strangely familiar... I have a feeling that I may have heard it a few time some years back...

Nicely done!
Logic is the beginning of wisdom.
Reply
#8
(02-21-2024, 06:38 AM)1micha.elok Wrote:
(02-20-2024, 06:52 PM)johnno56 Wrote: ... I am not expecting piano tones...
.. What I am expecting is a Synthesizer... 


Just curious with "do re mi" generated by SquareWave function, 
let's try this Pianoman ..... Cool

Code:
'----------------
' INITIALIZATION
'----------------
include "sfx.n7"
set window "Pianoman", 160, 200,false,3

constant DO = 261.63*2
constant RE = 293.66*2
constant MI = 329.63*2
constant FA = 349.23*2
constant SO = 392.00*2
constant LA = 440.00*2
constant TI = 493.88*2
constant b1 = 0.2
constant b2 = 0.4

visible sfx = SFX() 'create an sfx instance

'--------------
' MAIN PROGRAM
'--------------
wln "PIANOMAN"
wln
wln "1 - Play DO"
wln "2 - Play RE"
wln "3 - Play MI"
wln "4 - Play FA"
wln "5 - Play SO"
wln "6 - Play LA"
wln "7 - Play TI"
wln "8 - Play Song"
wln
wln "Esc - Quit"

while not keydown(KEY_ESCAPE)
    if keydown(KEY_1, true) then Piano(b1,DO)
    if keydown(KEY_2, true) then Piano(b1,RE)
    if keydown(KEY_3, true) then Piano(b1,MI)
    if keydown(KEY_4, true) then Piano(b1,FA)
    if keydown(KEY_5, true) then Piano(b1,SO)
    if keydown(KEY_6, true) then Piano(b1,LA)
    if keydown(KEY_7, true) then Piano(b1,TI)
    if keydown(KEY_8, true) then Song()
    wait 1
wend

'-----------
' FUNCTIONS
'-----------
function Piano(b,x)
    'SquareWave(duration,freq,volume)
    mytone = sfx.SquareWave(b,x,0.05)
    play sound mytone
    Pause()
endfunc

function Song()
    Piano(b2,SO);Piano(b1,SO);Piano(b1,SO)
    Piano(b2,SO);Piano(b2,MI)
    Piano(b2,DO*2);Piano(b1,DO*2);Piano(b1,DO*2)
    Piano(b2,TI);Piano(b2,LA)
   
    Piano(b2,SO);Piano(b1,SO);Piano(b1,SO)
    Piano(b2,LA);Piano(b1,SO);Piano(b1,SO)
    Piano(b2,SO); Piano(b1,FA); Piano(b1,MI)
    Piano(b2,RE); Piano(b1,DO); Piano(b1,RE)
   
    Piano(b2,MI); Piano(b1,MI); Piano(b1,MI)
    Piano(b2,RE); Piano(b2,RE)
    Piano(b1,DO); Piano(b1,RE); Piano(b1,MI);Piano(b1,DO)
    Piano(b1,LA/2);Piano(b1,TI/2);Piano(b2,DO)
   
    Piano(b2,SO/2);Piano(b1,DO); Piano(b1,RE)
    Piano(b2,SO); Piano(b1,FA); Piano(b1,MI)
    Piano(b2,RE); Piano(b1,RE); Piano(b1,RE); Piano(b2*2,DO)                                                   
endfunc

function Pause()
    wait 400
endfunc

Nice Smile  But you're creating new sound effects every time the user presses a key. Sound effect (and other resources, such as images) aren't garbage collected (they can't be). So the program will eat more and more memory. Better create the different piano sound effects once and re-use them, or release the old sound when a new one is created ('free sound').
Reply
#9
(02-21-2024, 04:07 PM)Marcus Wrote: ...
Nice Smile  But you're creating new sound effects every time the user presses a key. Sound effect (and other resources, such as images) aren't garbage collected (they can't be). So the program will eat more and more memory. Better create the different piano sound effects once and re-use them, or release the old sound when a new one is created ('free sound').

I made two experiments to see the memory's consumption.
I played the song "Waltzing Matilda" ten times in each experiment.
I'm glad that my computer had not exploded yet during the experiment   Cool

Waltzing Matilda, Waltzing Matilda
You'll come a waltzing Matilda with me
And he sang as he sat
And waited till his billy boiled
You'll come a waltzing Matilda with me

Just imagine, I sang it 20 times  Big Grin Big Grin 

Herewith the piece of code that makes a difference
Code:
'This function will play sound frequency x at duration b
function Piano(b,x)
    'SquareWave(duration,freq,volume)
    mytune = sfx.SquareWave(b,x,0.05)
    play sound mytune
    Pause()
    free sound mytune 'release sound from memeory
endfunc

The First Experiment (without free sound memory)
Commit (KB)             57.344
Working Set (KB)       59.164
Shareable (KB)            7.324
Private (KB)              51.840

The Second Experiment (with free sound memory)
Commit (KB)              26.816
Working Set (KB)        28.796
Shareable (KB)            7.440
Private (KB)               21.356

Thank you Marcus for your suggestion in free-ing the sound memory.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)