Problem with sound [Partially solved + includes a simple piano]

Trying to write a very simple piano/tune playing program but have come across a problem. The sound stops playing after a short period. It feels like some buffer is being met but am not sure


-- Simple Piano

-- Use this function to perform your initial setup
function setup()
    print("Hello Dear!")
    counter=0
    f=0.27
    
    --note frequency approximations
    --1.  c  = 0.2700
    --2.  c# = 0.2785
    --3.  d  = 0.2870
    --4.  d# = 0.2955
    --5.  e  = 0.3040
    --6.  f  = 0.3125
    --7.  f# = 0.3210
    --8.  g  = 0.3295
    --9.  g# = 0.3380
    --10. a  = 0.3465
    --11. a# = 0.3550
    --12. b  = 0.3635
    --13. c  = 0.3720
    
    song={1,0,0,3,5,0,0,1,5,0,1,0,5,0,0,0,3,0,0,5,6,6,5,3,6,0,0,0,0,0,0,0,
    5,0,0,6,8,0,0,5,8,0,5,0,8,0,0,0,6,0,0,8,10,10,8,6,10,0,0,0,0,0,0,0,8,
    0,0,1,3,5,6,8,10,0,0,0,0,0,0,0,10,0,0,3,5,6,8,10,12,0,0,0,0,0,0,0,
    12,0,0,5,6,8,10,12,13,0,0,0,0,0,10,6,5,0,1,0,8,0,5,0,8,0,6,0,5,0,3,0,1,0,0,0,0,0,0,0}
    songpos=1
    
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color
    background(40, 40, 50)
    text(f,WIDTH/2,HEIGHT/2)
    -- This sets the line thickness
    strokeWidth(5)
    if counter==10 then
        f=0
        if song[songpos]>0 then
            f=0.27-0.0085+song[songpos]*0.0085
        end
        
        sound({Waveform=SOUND_SINEWAVE,StartFrequency=f,SquareDuty=0.65,RepeatSpeed=0,DecayTime=0.2})
        
        counter=0
        songpos = songpos + 1
        if songpos>#song then songpos=1 end
        
    end    
    counter = counter + 1
end

Ok, it appears if you pre-play the sounds this remedies the problem

-- Simple Piano

-- Use this function to perform your initial setup
function setup()
    print("Hello Dear!")
    counter=0
    f=0.27
    
    --note frequency approximations
    --1.  c  = 0.2700
    --2.  c# = 0.2785
    --3.  d  = 0.2870
    --4.  d# = 0.2955
    --5.  e  = 0.3040
    --6.  f  = 0.3125
    --7.  f# = 0.3210
    --8.  g  = 0.3295
    --9.  g# = 0.3380
    --10. a  = 0.3465
    --11. a# = 0.3550
    --12. b  = 0.3635
    --13. c  = 0.3720
    
    song={1,0,0,3,5,0,0,1,5,0,1,0,5,0,0,0,3,0,0,5,6,6,5,3,6,0,0,0,0,0,0,0,
    5,0,0,6,8,0,0,5,8,0,5,0,8,0,0,0,6,0,0,8,10,10,8,6,10,0,0,0,0,0,0,0,8,
    0,0,1,3,5,6,8,10,0,0,0,0,0,0,0,10,0,0,3,5,6,8,10,12,0,0,0,0,0,0,0,
    12,0,0,5,6,8,10,12,13,0,0,0,0,0,10,6,5,0,1,0,8,0,5,0,8,0,6,0,5,0,3,0,1,0,0,0,0,0,0,0}
    
    songpos=1
    notes={}
    for i=0,12 do
        table.insert(notes,{Waveform=SOUND_SQUAREWAVE,StartFrequency=0.27+i*0.0085,SquareDuty=0.65,RepeatSpeed=0,DecayTime=0.2,SustainTime=0.2,SustainPunch=0.2,DecayTime=0.5})
    end
end

-- This function gets called once every fram,,
function draw()
    -- This sets a dark background color
    background(40, 40, 50)
    text(f,WIDTH/2,HEIGHT/2)
    -- This sets the line thickness
    strokeWidth(5)
    if counter==10 then
        if song[songpos]>0 then
            sound(notes[song[songpos]])
        end
        counter=0
        songpos = songpos + 1
        if songpos>#song then songpos=1 end
        
    end    
    counter = counter + 1
end


meritorious, i love it best

version two is much better. more reverb i like it. thanks for sharing.
but i hear cuts from the 26th with ipad air and codea 8.3.

@West this is great, but i hear some glitches. Can you hear them too?

@Jmv38 @West I heard glitches when I tried the code. iPad Air and latest code.

Yes, the glitches are still there - I suspect it might be something like new sound instances keep being generated and some sort of maximum is being hit (with the old ones not being deleted)

Also, the frequency of the notes is off - here is a slightly improved version but with a very nasty implementation of the white piano keys. Still not convinced they are the right pitch though

-- Simple Piano

-- Use this function to perform your initial setup
function setup()
    print("Hello Dear!")
    counter=0
    f=0.27
    
    --note frequency approximations - these are wrong!
    --1.  c  = 0.2700
    --2.  c# = 0.2785
    --3.  d  = 0.2870
    --4.  d# = 0.2955
    --5.  e  = 0.3040
    --6.  f  = 0.3125
    --7.  f# = 0.3210
    --8.  g  = 0.3295
    --9.  g# = 0.3380
    --10. a  = 0.3465
    --11. a# = 0.3550
    --12. b  = 0.3635
    --13. c  = 0.3720
    songplay=1
    song={1,0,0,3,5,0,0,1,5,0,1,0,5,0,0,0,3,0,0,5,6,6,5,3,6,0,0,0,0,0,0,0,
    5,0,0,6,8,0,0,5,8,0,5,0,8,0,0,0,6,0,0,8,10,10,8,6,10,0,0,0,0,0,0,0,8,
    0,0,1,3,5,6,8,10,0,0,0,0,0,0,0,10,0,0,3,5,6,8,10,12,0,0,0,0,0,0,0,
    12,0,0,5,6,8,10,12,13,0,0,0,0,0,10,6,5,0,1,0,8,0,5,0,8,0,6,0,5,0,3,0,1,0,0,0,0,0,0,0}
    whitenotes={1,3,5,6,8,10,12,13}
    songpos=1
    notes={}
    for i=0,12 do
        table.insert(notes,{Waveform=SOUND_SQUAREWAVE,StartFrequency=0.27+i*0.0095,SquareDuty=0.65,RepeatSpeed=0,DecayTime=0.2,SustainTime=0.2,SustainPunch=0.2,DecayTime=0.5})

    end
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color
    background(40, 40, 50)
    text(f,WIDTH/2,5*HEIGHT/6)
    -- This sets the line thickness
    strokeWidth(5)
    if counter==8 and songplay==1 then
        if song[songpos]>0 then
            sound(notes[song[songpos]])
        end
        counter=0
        songpos = songpos + 1
        if songpos>#song then songpos=1 end
        
    end    
    counter = counter + 1
    
    
    --only the whitenotes and a rough and nasty current touch use
    for i=0,8 do
        keywidth=WIDTH/8
        stroke(177, 173, 173, 255)
        fill(240, 239, 239, 255)
        rect(i*keywidth,200,keywidth,300)
            if CurrentTouch.x>i*keywidth and CurrentTouch.x<(i+1)*keywidth and CurrentTouch.y>200 and CurrentTouch.y<500 then
            sound(notes[whitenotes[i+1]])
        end
    end

    
end


A similar glitch with a simpler bit of code. Change counterlimit to speed up/slow down

-- Bell

-- Use this function to perform your initial setup
function setup()
    print("Hello bell!")
    bell=0
    dir=1
    counter=0
    counterlimit=2
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color
    background(40, 40, 50)
    -- This sets the line thickness
    strokeWidth(5)
    counter = counter + 1
    if counter>counterlimit then
        sound("Game Sounds One:Bell 2", 0.3, bell, 0, false)
        bell = bell +dir* 0.1
        if bell<0 or bell>2 then dir = dir * -1 end
        counter=0
    end
    -- Do your drawing here
    text(bell,WIDTH/2,HEIGHT/2)
end

if i use mp3 or ?

A further issue - moving between one of the asset sounds (with volume, pitch and pan control) and the procedural generation seems to break something. Can someone please test the following to confirm the bug

-- Sound Object

function setup()

end

function touched(touch)
    --Step 1 
    --run the program and tap screen to hear sounds
--    sound(SOUND_PICKUP, 44948)
    
    --Step 2 comment out previous line and comment in the following two lines then run program and tap screen - sounds with varying pitch
    
    
--        p=(touch.x/WIDTH)
-- sound("Game Sounds One:Bell 2",1.0,p,0.0,false)
    
    --Step 3 comment out previous two lines and comment in next line then run program and tap screen - no sound
    
--sound(SOUND_PICKUP, 44948)

end

@Simeon - any insights on what’s going on under the hood?

u could try mp3, http://pan.baidu.com/s/1pJGAqAf

@West I’ll give this a look and see what’s going on.

@firewolf - thanks but I don’t want to use MP3 (and am not sure if they are even supported)

@Simeon - great, thanks. If there is any documentation, particularly on the StartFrequency number range property (or the equivalent pitch property) and how it relates to the frequency of the generated waveform that’d be great too

@Simeon any luck on the sound function?

@West I ran your code above and the sound seems to work OK the way it’s coded. The last line that you say has no sound works fine for me. You say to tap the screen. One thing you might be overlooking is that when you tap the screen, the touched function is executed twice. Once for BEGAN, and once for ENDED. The first sound function is interrupted when the second sound function is executed.

@dave1707 - what ipad do you have? I still get the problem switching between the procedural and asset. However, if I encapsulate it around a check for began only as suggested then this seems to resolve the issue, so good spot.

@West I’m on an iPad Air with the latest code, iOS and Codea.

I’m on an iPad 3 so that might be a contributing factor

OK - think I’ve figured out the pitch command. Pitch=1 is unchanged. Pitch=2 is an octave higher and pitch=0.5 is an octave lower.

Here is a demo using a simple piano setup. Play the keys or play one of the 3 demo songs. Adjust the tempo and sound used.

-- Simple Piano
-- by West
displayMode(FULLSCREEN)
supportedOrientations(LANDSCAPE_ANY)
-- Use this function to perform your initial setup
function setup()
    counter=0
    songplay=0
    whitenotes={1,3,5,6,8,10,12,13,15,17,18,20,22,24,25,27,29}
    blacknotes={2,4,0,7,9,11,0,14,16,0,19,21,23,0,26,28}
    
    --number corresponds to the white and black(#/flats) above ; 0 is a rest
    songlist={}
    table.insert(songlist,{song={1,0,0,3,5,0,0,1,5,0,1,0,5,0,0,0,3,0,0,5,6,6,5,3,6,0,0,0,0,0,0,0,
    5,0,0,6,8,0,0,5,8,0,5,0,8,0,0,0,6,0,0,8,10,10,8,6,10,0,0,0,0,0,0,0,8,
    0,0,1,3,5,6,8,10,0,0,0,0,0,0,0,10,0,0,3,5,6,8,10,12,0,0,0,0,0,0,0,
    12,0,0,5,6,8,10,12,13,0,0,0,0,0,10,6,5,0,1,0,8,0,5,0,8,0,6,0,5,0,3,0,1,0,0,0,0,0,0,0},name="Do-Re-Me"})
    
    
    table.insert(songlist,{song={8,0,8,0,10,0,12,0,8,0,12,0,10,0,0,0,8,0,8,0,10,0,12,0,8,0,0,0,7,0,0,0,
    8,0,8,0,10,0,12,0,13,0,12,0,10,0,8,0,7,0,3,0,5,0,7,0,8,0,0,0,8,0,0,0
    },name="Yankee Doodle"})
    --yes the low G is an octave too high!
    table.insert(songlist,{song={5,0,5,0,6,0,8,0,8,0,6,0,5,0,3,0,1,0,1,0,3,0,5,0,5,0,0,3,3,0,0,0,
    5,0,5,0,6,0,8,0,8,0,6,0,5,0,3,0,1,0,1,0,3,0,5,0,3,0,0,1,1,0,0,0,
    3,0,3,0,5,0,1,0,3,0,5,6,5,0,1,0,3,0,5,6,5,0,3,0,1,0,3,0,8,0,0,0,
    5,0,5,0,6,0,8,0,8,0,6,0,5,0,3,0,1,0,1,0,3,0,5,0,3,0,0,1,1,0,0,0
    },name="Ode to joy"})
    
    songpos=1
    notes={}
    lastnote=0
    blackplayed=0
    tempo=9
    pitch=1
    numberofkeys=16
    currentsong=2
    currentinstrument=1
    instrument={}
    table.insert(instrument,{inst="Game Sounds One:Bell 2",name="Bell"})
    table.insert(instrument,{inst="Game Sounds One:Pop 1",name="Pop"})
    table.insert(instrument,{inst="Game Sounds One:Zapper 1",name="Zap"})
    table.insert(instrument,{inst="A Hero's Quest:Hurt 4",name="Ouch"})
    
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color
    background(40, 40, 50)
    if songplay==0 then
        sprite("Cargo Bot:Play Button",WIDTH*0.95,HEIGHT*0.975,0.1*WIDTH,-40)
    else
        sprite("Cargo Bot:Stop Button",WIDTH*0.95,HEIGHT*0.975,0.1*WIDTH,-40)
    end
    -- This sets the line thickness
    strokeWidth(5)
    fill(255)
    --display song name
    text(songlist[currentsong].name,WIDTH/2,HEIGHT*0.975)
    --display current instrument
    text(instrument[currentinstrument].name,WIDTH/2,HEIGHT*0.1)
    --display tempo and tempo control
    text("Tempo",WIDTH*0.95,HEIGHT*0.12)
    text(21-tempo,WIDTH*0.91,HEIGHT*0.04)
    sprite("Cargo Bot:Command Grab",WIDTH*0.95,HEIGHT*0.02,50,34)
    sprite("Cargo Bot:Command Grab",WIDTH*0.95,HEIGHT*0.06,50,-34)
    
    if counter>=tempo and songplay==1 then
        if songlist[currentsong].song[songpos]>0 then
            getPitch(songlist[currentsong].song[songpos])
        end
        counter=0
        songpos = songpos + 1
        if songpos>#songlist[currentsong].song then songpos=1 end
    end
    counter = counter + 1
    for i=0,numberofkeys do
        keywidth=WIDTH/numberofkeys
        stroke(177, 173, 173, 255)
        fill(240, 239, 239, 255)
        rect(i*keywidth,200,keywidth,300)
    end
    
    for b=0,numberofkeys do
        bkeywidth=WIDTH/numberofkeys
        stroke(149, 149, 149, 255)
        fill(48, 48, 48, 255)
        if b~=2 and b~=6 and b~=9 and b~=13 then
            rect(3*WIDTH/(numberofkeys*4)+b*bkeywidth,300,bkeywidth/2,200)
        end
    end
    
end

function touched(t)
    --check black notes
    blackplayed=0
    for b=0,numberofkeys do
        if t.x>3*WIDTH/(numberofkeys*4)+b*bkeywidth and t.x<3*WIDTH/(numberofkeys*4)+b*bkeywidth+bkeywidth/2 and t.y>300 and t.y<500 and b~=2 and b~=6 and b~=9 and b~=13 then
            if t.state==BEGAN then
                getPitch(blacknotes[b+1])
                lastnote=blacknotes[b+1]
            end
            if t.state==MOVING and lastnote~=blacknotes[b+1] then
                getPitch(blacknotes[b+1])
                lastnote=blacknotes[b+1]
            end
            blackplayed=1
        end
    end
    
    --check white notes
    for i=0,numberofkeys do
        keywidth=WIDTH/numberofkeys
        if t.x>i*keywidth and t.x<(i+1)*keywidth and t.y>200 and t.y<500 and blackplayed==0 then
            if t.state==BEGAN and lastnote==0 then
                getPitch(whitenotes[i+1])
                lastnote=whitenotes[i+1]
                blackplayed=0
            elseif t.state==MOVING and (whitenotes[i+1]~=lastnote and blackplayed==0) then
                getPitch(whitenotes[i+1])
                lastnote=whitenotes[i+1]
                blackplayed=0
            end
        end
    end
    
    if t.state==ENDED or t.y>500 or t.y<200 then
        lastnote=0
        blackplayed=0
    end
    
    --start/stop button for song
    if t.state==ENDED and t.x>WIDTH*0.9 and t.y>HEIGHT*0.9 then
        songplay = songplay + 1
        if songplay>1 then
            songplay=0
            songpos=1
        end
    end
    --advance to next song
    if t.state==ENDED and t.x<WIDTH*0.9 and t.y>HEIGHT*0.9 then
        songpos=1
        currentsong = currentsong + 1
        if currentsong>#songlist then currentsong=1 songpos=1 end
    end
    
    --change instrument
    if t.state==ENDED and t.y<HEIGHT*0.1 and t.x<WIDTH*0.9 then
        currentinstrument = currentinstrument + 1
        if currentinstrument>#instrument then
            currentinstrument=1
        end
    end
    
    -- change tempo
    if t.state==ENDED and t.x>WIDTH*0.9 and t.y<HEIGHT*0.1 then
        if t.y<HEIGHT*0.05 then
            tempo = tempo +1
            if tempo>20 then tempo=20 end
        else
            tempo = tempo - 1
            if tempo<1 then tempo=1 end
        end
        
        
    end
end

function getPitch(n)
    --octave shift
    octaveshift=2
    pitch=2^(((n-1)/12)-octaveshift)
    --a pitch of 1 is the base pitch of the sound.
    --a pitch of 2 is one octave higher
    --a pitch of 0.5 is one octave lower
    sound(instrument[currentinstrument].inst,1.0,pitch,0.0,false)
end

Hmm, the way the pitches are set up makes it difficult to make an instrument. Would be better if it was set up how you first thought, where you could type in the actual frequency.

I have been messing with a game that uses sound, but I need to make a guitar sound which I can’t figure out. Using WAVEFORM_SINEWAVE for generating sound seems to lag the game for a half second every time a new sound is generated, so that option is out. Probably going to have to use .wav samples and use the sound() function, which doesn’t lag.