Using coroutines together with audio.play breaks the coroutine.

Recently I’ve discovered this topic: https://forums.coronalabs.com/topic/29490-audioplay-not-calling-oncomplete-when-called-in-coroutine/ after encountering the problem in my own code.

Apparently ‘audio.play’ can’t be used when called with the ‘onComplete’ param and within a coroutine.
There’s a solution in the topic, but since this is not documented anywhere, I’m worried there might be other cases which could break when called inside a coroutine.

Could anyone from the community or any Corona developer explain me which API’s not to use when inside a coroutine? Or are there any plans on fixing it? Since it was noted as a ‘bug’ by moderator Joshua Quick

it’s “convenient” to think of coroutines as threads, but they’re not really, just states, and multiple-states-within-a-single-thread really isn’t a problem for lua, while multiple-threads are (or can be, they’re at least “tricky” to manage).  so i would *guess* (tho not even experimentally tested) that the only api’s potentially likely to cause trouble are ones that might be running on truly separate threads, which implies lower-level code (at least Java, if not native).  audio, of course, seems a prime candidate.  other “simple” stuff (ie, lua-based) with callbacks (fe transition.to, or timer.performWithDelay) probably wouldn’t pose a problem.  so i’d watch out for lower-level stuff that might need the same *thread* not just the same *state*, fe i’d be cautious with the network lib vs coroutines, or system or Runtime (fe, i’d guess Runtime.dispatchEvent might have problems).

again, i haven’t actually tested any of these guesses, so beware, ymmv.

personally I prefer enterFrame() to co-routines… easier to debug and less likely to break

I guess your right! I’m gonna do some tests myself to verify which API, probably limited by mostly lower-level stuff like io.*, audio .*

I very much agree enterFrame listeners are a better solution most of the time, but in this specific use-case I use them a little differently, where an enterFrame listener isn’t really a solution. I’m using it as syntactic sugar actually. I use them in almost all of my cutscenes.

I created a small module which allows me to avoid the tangle of nested onCompletes, usage explained in the psuedo code below (which is of course a very simple example, real cutscenes can be 20x as long)

sleepable:run(function(continueCutscene) player:showDialog("Hey there!"):onHidden(continueCutscene) coroutine.yield() player:walkToCenterOfXY(continueCutscene) coroutine.yield( player:showDialog("How are you doing?"):onHidden(continueCutscene) coroutine.yield() otherGuy:rotate() timer.performWithDelay(1000, function() otherGuy:showDialog("Oh hi, doing great!"):onHidden(function() otherGuy:showSpeechBubble("happy", continueCutscene) end) end) coroutine.yield() otherGuy:showDialog("But what was your name again?"):onHidden(continueCutscene) coroutine.yield() timer.performWithDelay(100, function() keyboardOverlayBuilder:new("What's your name?") end) end)

VS

player:showDialog("Hey there!"):onHidden(function() player:walkToCenterOfXY(function() player:showDialog("How are you doing?"):onHidden(function() otherGuy:rotate() timer.performWithDelay(1000, function() otherGuy:showDialog("Oh hi, doing great!"):onHidden(function() otherGuy:showSpeechBubble("happy", function() otherGuy:showDialog("But what was your name again?"):onHidden(function() timer.performWithDelay(100, function() keyboardOverlayBuilder:new("What's your name?") end) end) end) end) end) end) end) end)

I would probably use a mini-script for something that complicated…

Maybe something like (pseudo as well)

{1, object, action, goto2}, {2, object, action, goto3},

Then schedule that with a single enterFrame()

it’s “convenient” to think of coroutines as threads, but they’re not really, just states, and multiple-states-within-a-single-thread really isn’t a problem for lua, while multiple-threads are (or can be, they’re at least “tricky” to manage).  so i would *guess* (tho not even experimentally tested) that the only api’s potentially likely to cause trouble are ones that might be running on truly separate threads, which implies lower-level code (at least Java, if not native).  audio, of course, seems a prime candidate.  other “simple” stuff (ie, lua-based) with callbacks (fe transition.to, or timer.performWithDelay) probably wouldn’t pose a problem.  so i’d watch out for lower-level stuff that might need the same *thread* not just the same *state*, fe i’d be cautious with the network lib vs coroutines, or system or Runtime (fe, i’d guess Runtime.dispatchEvent might have problems).

again, i haven’t actually tested any of these guesses, so beware, ymmv.

personally I prefer enterFrame() to co-routines… easier to debug and less likely to break

I guess your right! I’m gonna do some tests myself to verify which API, probably limited by mostly lower-level stuff like io.*, audio .*

I very much agree enterFrame listeners are a better solution most of the time, but in this specific use-case I use them a little differently, where an enterFrame listener isn’t really a solution. I’m using it as syntactic sugar actually. I use them in almost all of my cutscenes.

I created a small module which allows me to avoid the tangle of nested onCompletes, usage explained in the psuedo code below (which is of course a very simple example, real cutscenes can be 20x as long)

sleepable:run(function(continueCutscene) player:showDialog("Hey there!"):onHidden(continueCutscene) coroutine.yield() player:walkToCenterOfXY(continueCutscene) coroutine.yield( player:showDialog("How are you doing?"):onHidden(continueCutscene) coroutine.yield() otherGuy:rotate() timer.performWithDelay(1000, function() otherGuy:showDialog("Oh hi, doing great!"):onHidden(function() otherGuy:showSpeechBubble("happy", continueCutscene) end) end) coroutine.yield() otherGuy:showDialog("But what was your name again?"):onHidden(continueCutscene) coroutine.yield() timer.performWithDelay(100, function() keyboardOverlayBuilder:new("What's your name?") end) end)

VS

player:showDialog("Hey there!"):onHidden(function() player:walkToCenterOfXY(function() player:showDialog("How are you doing?"):onHidden(function() otherGuy:rotate() timer.performWithDelay(1000, function() otherGuy:showDialog("Oh hi, doing great!"):onHidden(function() otherGuy:showSpeechBubble("happy", function() otherGuy:showDialog("But what was your name again?"):onHidden(function() timer.performWithDelay(100, function() keyboardOverlayBuilder:new("What's your name?") end) end) end) end) end) end) end) end)

I would probably use a mini-script for something that complicated…

Maybe something like (pseudo as well)

{1, object, action, goto2}, {2, object, action, goto3},

Then schedule that with a single enterFrame()

Just ran into this issue and burned half a day trying to figure out the cause. I suspected the issue was because the coroutine.yield was preventing the callback but it appears the issue is deeper as stated above and in the earlier discussion at audio.play not calling onComplete when called in coroutine? (this is the link in the new Solar 2D forum).

I found the simplest solution to playing 2 sounds consecutively was simply to call my playAudio function with the first track and then call timer.performWithDelay(audio.getDuration (<track1 handle>), <listener function>), where my listener function calls my playAudio function, passing to it the track to play form the local context.
The second call is delayed by the duration of the first track’s duration.

@Edissey

You should post a new thread. This thread is 3 years old and not really related.