Audio: Transition Between 2 Storyboard Scenes Using Back Button

Howdy,

I have an issue currently where I’ve tried all the codes and look at the API and I can’t figure it out.  I’ve tried to mute and then pause things, and separate out the channels… need help!

I have intro BG music when the app loads and plays on all scenes, menus, etc. until you enter a level at which point it triggers a new song to go along with the theme of the level.  Currently I can get the music to play and then change in the level,  however my problem is the transition back to the levelSelect scene (trigger the introMusic again/ kill levelMusic) and when I go select the level again it plays the levelMusic again.  I’m sure this has to do with how it’s stored in memory or disposed of I just haven’t found the magic code yet.

so this code will get it to play both but when i go back to the levelselect it continues to play the level01 music and then stops all together when i loop back into the game again… my head hurts

here is the base code on the  “menu.lua”  scene:

[lua]

local storyboard = require( “storyboard” )

local scene = storyboard.newScene()

local widget = require( “widget” )


local intromusic = audio.loadStream( “perky.mp3” , true)

    local playIntroMusic = function()

        audio.play( intromusic, {channel=2, loops=-1 } ) – 

        audio.setVolume( 0.75, { channel=2 } ) 

    end

    local playIntroMusic = timer.performWithDelay(1, playIntroMusic, 1)


– forward declarations and other locals

local playBtn

local function onPlayBtnRelease()

    – go to levels.lua scene

    storyboard.gotoScene( “levels”, “slideLeft”, 200 )

    return true

end

– Called when the scene’s view does not exist:

function scene:createScene( event )

    local group = self.view

    

    playBtn = widget.newButton{

        defaultFile=“play.png”,

        overFile=“play_d.png”,

        width=350, height=100,

        onRelease = onPlayBtnRelease

    }

    playBtn:setReferencePoint( display.CenterReferencePoint )

    playBtn.x = display.contentWidth * 0.5

    playBtn.y = 300

    group:insert( playBtn )  

    

end

– Called immediately after scene has moved onscreen:

function scene:enterScene( event )

    local group = self.view

        

end

– Called when scene is about to move offscreen:

function scene:exitScene( event )

    local group = self.view    

end

– If scene’s view is removed, scene:destroyScene() will be called just prior to:

function scene:destroyScene( event )

    local group = self.view

    

    if playBtn then

        playBtn:removeSelf()    – widgets must be manually removed

        playBtn = nil

    end

end


scene:addEventListener( “createScene”, scene )

scene:addEventListener( “enterScene”, scene )

scene:addEventListener( “exitScene”, scene )

scene:addEventListener( “destroyScene”, scene )


return scene

[/lua]

here is the level select menu “levels.lua” :

[lua]

local storyboard = require( “storyboard” )

local scene = storyboard.newScene()

– include Corona’s “widget” library

local widget = require( “widget” )

local backSound = audio.loadSound( “click_back.wav” )

local function onbackBtnPress()

        audio.play( backSound )

    end    

    

local levelSound = audio.loadSound( “tap.wav” )

local function onlevelBtnPress()

        audio.play( levelSound )

    end

local lockSound = audio.loadSound( “tap_locked.wav” )

local function onlockBtnPress()

        audio.play( lockSound )

    end        

    

– forward declarations and other locals

local lvl1_Btn

local backBtn

–level buttons that go to each level

local function onlvl1_BtnRelease()    

    storyboard.gotoScene( “levels.level001”, “crossFade”, 200 )

    audio.fadeOut( intromusic, { channel=2, time=1000 } )    

    return true    – indicates successful touch

end

local function onbackBtnRelease()

    – go to levels.lua scene

    storyboard.gotoScene( “menu”, “slideRight”, 200 )    

    return true

end


– BEGINNING OF YOUR IMPLEMENTATION 


– Called when the scene’s view does not exist:

function scene:createScene( event )

    local group = self.view

    – create a widget button (which will loads levels.lua on release)

    local backBtn = widget.newButton{

        defaultFile=“lvl_header.png”,

        width=768, height=86,

        onPress = onbackBtnPress,

        onRelease = onbackBtnRelease    – event listener function

    }

    backBtn:setReferencePoint( display.CenterReferencePoint )

    backBtn.x = display.contentWidth * 0.5

    backBtn.y = 65

    

    – create a widget button (which will loads level1.lua on release)

    local lvl1_Btn = widget.newButton{

        label= “1”,

        id = “button1”,

        font = “Berlin Sans FB Demi”,

        labelColor = { default = { 82, 119, 139, 255 }, over= { 100, 145, 169, 255 } },

        fontSize = 64,

        offset = -4,        

        defaultFile=“levelbox.png”,

        overFile=“levelbox_down.png”,

        width=122, height=110,

        onPress = onlevelBtnPress,    

        onRelease = onlvl1_BtnRelease    – event listener function

    }    

    lvl1_Btn:setReferencePoint( display.CenterReferencePoint )

    lvl1_Btn.x = 100

    lvl1_Btn.y = 250    

    

    – all display objects must be inserted into group

    group:insert( backBtn )

    group:insert( lvl1_Btn )

end

– Called immediately after scene has moved onscreen:

function scene:enterScene( event )

    local group = self.view        

end

– Called when scene is about to move offscreen:

function scene:exitScene( event )

    local group = self.view    

end

– If scene’s view is removed, scene:destroyScene() will be called just prior to:

function scene:destroyScene( event )

    local group = self.view    

            if backBtn then

        backBtn:removeSelf()    – widgets must be manually removed

        backBtn = nil

    end

            if lvl1_Btn then

        lvl1_Btn:removeSelf()    – widgets must be manually removed

        lvl1_Btn = nil

    end

end


– END OF YOUR IMPLEMENTATION


scene:addEventListener( “createScene”, scene )

scene:addEventListener( “enterScene”, scene )

scene:addEventListener( “exitScene”, scene )

scene:addEventListener( “destroyScene”, scene )


return scene

[/lua]

here is the base code on the  “menu.lua”  scene:

[lua]

local storyboard = require( “storyboard” )

local scene = storyboard.newScene()

– include Corona’s “widget” library

local widget = require( “widget” )

local levelmusic = audio.loadStream( “slip.mp3” , true)

    local playLevelMusic = function()

        audio.play( levelmusic, { channel=3, loops=-1 } ) 

        audio.setVolume( 0.75, { channel=3 })

    end

    local playLevelMusic = timer.performWithDelay(1, playLevelMusic, 1)

local function onbackBtnRelease()

    – go to levels.lua scene

    storyboard.purgeScene(“levels.level001”)

    storyboard.gotoScene( “levels”, “slideRight”, 200 )

    audio.stop( playLevelMusic )

    audio.dispose( playLevelMusic )

    local playIntroMusic = timer.performWithDelay(1, playIntroMusic, 1)

    return true    – indicates successful touch

end

local backSound = audio.loadSound( “click_back.wav” )

local function onbackBtnPress()

        audio.play( backSound )

    end    


– BEGINNING OF YOUR IMPLEMENTATION


function scene:createScene( event )

    local group = self.view

    local backBtn = widget.newButton{

        defaultFile=“lvl_bar.png”,

        width=678, height=64,

        onPress = onbackBtnPress,

        onRelease = onbackBtnRelease    – event listener function

    }

    backBtn:setReferencePoint( display.CenterReferencePoint )

    backBtn.x = display.contentWidth * 0.5

    backBtn.y = 65

    group:insert( backBtn )        

end

– Called immediately after scene has moved onscreen:

function scene:enterScene( event )

    local group = self.view

end

– Called when scene is about to move offscreen:

function scene:exitScene( event )

    local group = self.view

    storyboard.purgeScene( “level001” )

end

– If scene’s view is removed, scene:destroyScene() will be called just prior to:

function scene:destroyScene( event )

    local group = self.view

        if backBtn then

        backBtn:removeSelf()    – widgets must be manually removed

        backBtn = nil

    end

end


– END OF YOUR IMPLEMENTATION


scene:addEventListener( “createScene”, scene )

scene:addEventListener( “enterScene”, scene )

scene:addEventListener( “exitScene”, scene )

scene:addEventListener( “destroyScene”, scene )


return scene

[/lua]

The problem you’re running into we covered in a recent blog post:

http://www.coronalabs.com/blog/2013/04/02/cleaning-up-display-objects-andlisteners/

While the post’s title makes it sound like its about display objects, there is significant information in there about how storyboard scenes are loaded and unloaded in particular how it impact audio.

Basically a scene is executed only once, when it’s first required in (the very first time that storyboard.gotoScene() is called.  On scene re-entry, the createScene() and enterScene() are re-executed based on if the scene has been purged or not.   Since your line 11 is outside any scene functions (or targeted from within), it will only execute the very first time until that scene is removed/un-required.  

Just put that timer call in line 11 inside of the “enterScene” function

that makes sense, I didn’t see that part of the article on Audio.  I’ll have to play around with this as I’ve got a couple menus and things and need to make sure I dispose the audio properly.

The problem you’re running into we covered in a recent blog post:

http://www.coronalabs.com/blog/2013/04/02/cleaning-up-display-objects-andlisteners/

While the post’s title makes it sound like its about display objects, there is significant information in there about how storyboard scenes are loaded and unloaded in particular how it impact audio.

Basically a scene is executed only once, when it’s first required in (the very first time that storyboard.gotoScene() is called.  On scene re-entry, the createScene() and enterScene() are re-executed based on if the scene has been purged or not.   Since your line 11 is outside any scene functions (or targeted from within), it will only execute the very first time until that scene is removed/un-required.  

Just put that timer call in line 11 inside of the “enterScene” function

that makes sense, I didn’t see that part of the article on Audio.  I’ll have to play around with this as I’ve got a couple menus and things and need to make sure I dispose the audio properly.

I’m having huge problems getting one audio to start and another to stop in a simple scene transition.

having peppered the code with print commands, I see that the create scene function in the second scene runs before the destroy scene function in the exiting scene.

this seems to mean that my audio.stop call in destroy scene stops the audio in the new scene.

That’s right.  You can’t destroy the scene that’s on the screen.  The new scene has to be created, then transitioned onto the screen and after that destroy gets called.

enterScene and exitScene are your friends here.  Stop it in exitScene and start it in enterScene.

cool!  :D really appreciate the reply.

in the simplest case, might it be better to use ‘enter’ & ‘exit’ rather than ‘create’ & ‘destroy’ – with the same “storyboard.purgeOnSceneChange = true” call every time?

what really is the difference?!

i know ‘destroy’ purges the ‘group’.  but the audio hangs around and is so much harder to get rid off.  my thinking is that i would happily “remove” everything else too!

cheers :slight_smile:

I’m having huge problems getting one audio to start and another to stop in a simple scene transition.

having peppered the code with print commands, I see that the create scene function in the second scene runs before the destroy scene function in the exiting scene.

this seems to mean that my audio.stop call in destroy scene stops the audio in the new scene.

That’s right.  You can’t destroy the scene that’s on the screen.  The new scene has to be created, then transitioned onto the screen and after that destroy gets called.

enterScene and exitScene are your friends here.  Stop it in exitScene and start it in enterScene.

cool!  :D really appreciate the reply.

in the simplest case, might it be better to use ‘enter’ & ‘exit’ rather than ‘create’ & ‘destroy’ – with the same “storyboard.purgeOnSceneChange = true” call every time?

what really is the difference?!

i know ‘destroy’ purges the ‘group’.  but the audio hangs around and is so much harder to get rid off.  my thinking is that i would happily “remove” everything else too!

cheers :slight_smile: