Thread Rating:
  • 1 Vote(s) - 5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
N7 version 24.01.19 released
#1
Small update, just a new function (but a fun one) and some more examples: https://naalaa.com/n7/N7_240119.zip

2024-01-19
  • Added the 'createsound' function and 'create sound' command
  • Added the game Space Race (spacerace.n7) and Bomb Sweeper (bomb_sweeper.n7) to the examples/other folder

An example of the 'createsound' function, create_sound.n7, can be found under examples/help. As with all other commands and functions you can write 'createsound' in NED, put the caret somewhere in the function name, and press F1 to see the syntax and F1 again to load an example in a new tab.

In create_sound.n7 I've implemented three helper functions to create noise sounds (for explosions, guns etc) and sine wave and square wave sounds (useful for simple beeps, boops and boings). 'createsound' itself only takes 3 parameters: an array with data for the left channel, an array with data for the right channel and a sample rate.

Code:
' create_sound.n7
' ---------------

#win32

' Create some sound effects. Look at the implementations of CreateSineSfx, CreateSquareSfx and
' CreateNoiseSfx to see how 'createsound' works.
sampleRate = 11025
explosionSnd = CreateNoiseSfx(0.6, 0.9, 0, sampleRate)
laserShotSnd = CreateSquareSfx(0.25, 500, 100, 0.75, sampleRate)
jumpSnd = CreateSineSfx(0.2, 50, 600, 0.9, sampleRate)
pickupSnd = CreateSineSfx(0.1, 1500, 1500, 0.2, sampleRate)
gunShotSnd = CreateNoiseSfx(0.2, 1.2, 0, sampleRate)

' Create a window and output some info.
set window "Create sound", 640, 480
wln "1 - Explosion"
wln "2 - Laser shot"
wln "3 - Jump"
wln "4 - Pickup"
wln "5 - Gun shot"

' Loop until user presses escape.
while not keydown(KEY_ESCAPE)
    ' Use the numeric keys to play different sound effects.
    if keydown(KEY_1, true) play sound explosionSnd
    if keydown(KEY_2, true) play sound laserShotSnd
    if keydown(KEY_3, true) play sound jumpSnd
    if keydown(KEY_4, true) play sound pickupSnd
    if keydown(KEY_5, true) play sound gunShotSnd
    ' Sleep some.
    wait 16
wend

' CreateSineSfx
' -------------
' Create a sine wave sound effect. 'duration' is the duration of the sound in seconds. 'startFreq'
' is the frequency at the start of the effect and 'endFreq' is the frequency at the end. You can
' use different values of 'startFreq' and 'endFreq' to create slide effects. 'fadeOut' determines
' when/if the sound should start fading out. If 'fadeOut' is 0, the fade out starts immediately, and
' if it's 1 there is no fade out. 'sampleRate' (samples per second) should be in the range [8000 ..
' 22050]. N7 outputs audio at a sample rate of 22050, so higher values than that makes no sense.
function CreateSineSfx(duration, startFreq, endFreq, fadeOut, sampleRate)
    data = []
    a = 0
    da = 2*PI*startFreq/sampleRate
    dda = (2*PI*endFreq/sampleRate - 2*PI*startFreq/sampleRate)/(duration*sampleRate)
    vol = 1
    fadeOut = fadeOut*duration*sampleRate
    fadeOutDelta = 1/(duration*sampleRate - fadeOut)
    for i = 0 to duration*sampleRate - 1
        data[i] = sin(a)*vol
        a = a + da
        da = da + dda
        if i > fadeOut  vol = vol - fadeOutDelta
    next
    ' 'createsound(leftData, rightData, sampleRate)' creates a new sound and returns a sound id.
    ' 'leftData' is an array with data for the left channel and 'rightData' is for the right
    ' channel. The values in the arays should be in the range [-1..1]. 'sampleRate' is the number
    ' of samples per second, So if you want to create a sound that lasts for three seconds with a
    ' sample rate of 11025, the data arrays should contain 33075 (11025*3) elements each.
    '  You can also use 'create sound sound_id, leftData, rightData, sampleRate' if you want to
    ' use your own sound id.
    return createsound(data, data, sampleRate)
endfunc

' CreateSquareSfx
' ---------------
' Same as CreateSineSfx but using a square wave.
function CreateSquareSfx(duration, startFreq, endFreq, fadeOut, sampleRate)
    data = []
    a = 0
    da = 2*PI*startFreq/sampleRate
    dda = (2*PI*endFreq/sampleRate - 2*PI*startFreq/sampleRate)/(duration*sampleRate)
    vol = 1
    fadeOut = fadeOut*duration*sampleRate
    fadeOutDelta = 1/(duration*sampleRate - fadeOut)
    for i = 0 to duration*sampleRate - 1
        ' No, using sin here is stupid.
        sa = sin(a)
        if sa < 0  sa = -1
        elseif sa > 0 sa = 1
        data[i] = sa*vol
        a = a + da
        da = da + dda
        if i > fadeOut  vol = vol - fadeOutDelta
    next
    ' 'createsound(leftData, rightData, sampleRate)' creates a new sound and returns a sound id.
    ' 'leftData' is an array with data for the left channel and 'rightData' is for the right
    ' channel. The values in the arays should be in the range [-1..1]. 'sampleRate' is the number
    ' of samples per second, So if you want to create a sound that lasts for three seconds with a
    ' sample rate of 11025, the data arrays should contain 33075 (11025*3) elements each.
    '  You can also use 'create sound sound_id, leftData, rightData, sampleRate' if you want to
    ' use your own sound id.
    return createsound(data, data, sampleRate)
endfunc

' CreateNoiseSfx
' --------------
' Create a noise sound effect. 'duration' is the duration of the sound in seconds. A 'pitch' value
' < 0 increases the lower noise frequencies, while a value > 1 increases the higher frequencies.
' 'fadeOut' determines when/if the sound should start fading out. If 'fadeOut' is 0, the fade out
' starts immediately, and if it's 1 there is no fade out. 'sampleRate' (samples per second) should
' be in the range [8000 .. 22050]. N7 outputs audio at a sample rate of 22050, so higher values than
' that makes no sense.
function CreateNoiseSfx(duration, pitch, fadeOut, sampleRate)
    assert sampleRate >= 8000, "CreateBoomSfx: invalid sample rate"
    assert pitch > 0, "CreateBoomSfx: invalid pitch"
 
    ' Mix four different noise frequencies weighted, in a weird way, by the pitch value.
    freqs = [
            [v: 0, p: sampleRate/500, d: 0, t: 0, w: pitch],
            [v: 0, p: sampleRate/1000, d: 0, t: 0, w: pitch^2],
            [v: 0, p: sampleRate/2000, d: 0, t: 0, w: pitch^3],
            [v: 0, p: sampleRate/8000, d: 0, t: 0, w: pitch^4]]
   
    s = sizeof(freqs)
    data = []
    vol = 1
    fadeOut = fadeOut*duration*sampleRate
    fadeOutDelta = 1/(duration*sampleRate - fadeOut)
    for i = 0 to duration*sampleRate - 1
        v = 0
        w = 0
        for j = 0 to s - 1; f = freqs[j]
            f.t = f.t - 1
            if f.t <= 0
                f.t = f.p
                f.d = ((rnd()*2 - 1) - f.v)/f.p
            endif
            f.v = f.v + f.d
            v = v + f.v*f.w
            w = w + f.w
        next
        data[i] = vol*v/w
        if i > fadeOut  vol = vol - fadeOutDelta
    next
    ' 'createsound(leftData, rightData, sampleRate)' creates a new sound and returns a sound id.
    ' 'leftData' is an array with data for the left channel and 'rightData' is for the right
    ' channel. The values in the arays should be in the range [-1..1]. 'sampleRate' is the number
    ' of samples per second, So if you want to create a sound that lasts for three seconds with a
    ' sample rate of 11025, the data arrays should contain 33075 (11025*3) elements each.
    '  You can also use 'create sound sound_id, leftData, rightData, sampleRate' if you want to
    ' use your own sound id.
    return createsound(data, data, sampleRate)
endfunc
Reply
#2
Just downloaded the new version... quickly ran the sfx demo from the forum. Very retro. I like it... It's almost 1am... I will check it out some more after a brief nap or two...

Nicely done.

J
Logic is the beginning of wisdom.
Reply
#3
Love the create sound function Marcus. I'm just making a quick program to experiment with changing the various parameters for the sound creation functions, so that it is easier and quicker to experiment with them, without having to recompile each time. I'll hopefully post something shortly.
Reply
#4
Great to hear!

The functions I wrote aren't very advanced - you can't create that many fun effects with them. One could experiment with different data for the left and right channels, write echo and reverb stuff and a lot more. Sound isn't really my area of expertise, but I'll probably come up with some more advanced stuff when I get the time.

I'll check out your program!
Reply
#5
The sound function is very cool, this saves me from having to use an external program for the sounds. Smile
Reply
#6
I am curious... There does not seem to be a "library" for "createsound()". How did you manage to get N7 to create sounds? (if this is a "herbs and spices" kind of thing, then just ignore the request... lol)

J
Logic is the beginning of wisdom.
Reply
#7
(01-21-2024, 01:56 AM)johnno56 Wrote: I am curious... There does not seem to be a "library" for "createsound()". How did you manage to get N7 to create sounds? (if this is a "herbs and spices" kind of thing, then just ignore the request... lol)

J

Sometimes a library isn't enough. I could have solved it directly in n7 by generating a temporary wav file and loading it with 'loadsound', but that felt dirty, so I decided to add a function to the core of the language instead. Its communication with the runtime is implemented in source/syscmd.c (CreateSound) and the actual work in source/audio_portaudio.c (AUD_CreateSound and BuildSoundData - also used when converting data from wav files to n7's fixed sample rate). That's why I had to release a new version of n7 for this to work Smile

I'm planning to write a library, written in n7, that uses 'createsound' to generate cool sound effects. The functions I wrote for the example (CreateSineSfx, CreateSquareSfx and CreateNoiseSfx), also used in denaalaafender, were just a test of 'createsound'.
Reply
#8
Cool... Thank you for the explanation. Best of luck with the library... Smile

J
Logic is the beginning of wisdom.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)