No trouble at all. Yeah, coroutines are of limited use, but sort of nice for keeping some kinds of code more understandable I guess.
I switched to a non-coroutine approach, but for anyone else who reads this and still wants to use coroutines, I figured out a workaround for this problem. (Also, maybe Corona’s developers could adapt this workaround to easily fix the problem “under the hood” without a massive overhaul of Corona?)
The workaround works by guaranteeing that all callbacks happen in the main thread. It achieves this by detecting when a callback is being registered in a coroutine, and ‘postponing’ the call for the main thread to handle later. This is done via a timer, whose callbacks, unlike audio callbacks, seem to work when registered in a coroutine. (At least in the simulator.)
function runInMainThread ( func, packedArgs )
local thread = coroutine.running()
if thread then
timer.performWithDelay( 1,
function ( event )
local r
-- We're in the main thread now.
r = { func(unpack(packedArgs,1,table.maxn(packedArgs))) }
-- Let the coroutine know the result of the function call:
coroutine.resume ( thread, r )
end )
return unpack ( coroutine.yield() ) -- Wait for function to be called.
else
-- This is the main thread, so call normally.
return func ( unpack ( packedArgs, 1, table.maxn(packedArgs) ) )
end
end
So a “coroutine-safe” version of audio.play might look like:
function cosafe\_audioplay( h, params )
if coroutine.running() and params and params.onComplete then
runInMainThread ( audio.play, { h, params } )
else
audio.play( h, params )
end
end
And a blocking version of audio.play (for calling within a coroutine) could be written like this:
function blocking\_audioplay( h, params )
-- Only allow when called in a coroutine.
local thread = coroutine.running()
if thread then
-- Save the caller's onComplete and replace with our own.
if not params then params = {} end
orig\_onComplete = params.onComplete
-- Our new onComplete will be called in the main thread.
params.onComplete = function ( event )
if orig\_onComplete then
orig\_onComplete ( event )
end
coroutine.resume( thread )
end
cosafe\_audioplay ( h, params )
coroutine.yield()
end
end
And then code like this works as expected:
local h1 = audio.loadSound( "sound1.mp3" )
local h2 = audio.loadSound( "sound2.mp3" )
local h3 = audio.loadSound( "sound3.mp3" )
function cofunction()
-- These sounds will play back to back now.
blocking\_audioplay( h1 )
blocking\_audioplay( h2, { onComplete=function() print("sound 2 done.") end } )
blocking\_audioplay( h3 )
end
local thread = coroutine.create( cofunction )
coroutine.resume( thread )
Cheers,
bc [import]uid: 131050 topic_id: 35305 reply_id: 140487[/import]