Fileplayer "bad loadlen" leads to crash

,

Hi y'all!

Just "finished" a small game, but I've been seeing behavior throughout the entirety of the project related to "bad loadlen" errors being thrown over and over again, and I believe they have something to do with fileplayer. Something about garbage collection, CPU or memory retention.

Game will freeze up and then crash, most notably in roomFour() in the roomFourFinished conditional statement (I literally had to make this crash a feature because I couldn't resolve it. I have audio that plays saying "press A i'm going to crash soon press A" over and over until it crashes a few seconds later. Pressing A will move you to the next room).

This is with mp3s, but I've tried to resolve with wav ADPCM instead. Per the following post: Music, Sound, Animation & Image auditioning app - #21 by matt, I have also tried fileplayer:setStopOnUnderrun() and setting a buffer when initializing a new fileplayer, but both of those actually make the issue worse. This issue is unfathomably worse in simulator and can occur in a number of places, but really only occurs in roomFour after moving the shower curtain all the way over on hardware. In simulator, it can occur even right after the very first mp3 plays, which is right after cranking through the title screen lock frames.

I would love to resolve this behavior so I can fix my semi-finished project. Here's a snapshot of roomFour code, but I've attached the full .pdx as well:


function fade(fadeTime, --[[optional]]imageSource)
	local blackImage = gfx.image.new(imageSource or "Images/fade/black.png")
	blackImage:draw(0, 0)
	playdate.wait(fadeTime)
end

function roomFourSetUp()
    gfx.sprite.removeAll()
    gfx.clear()
    playdate.stopAccelerometer()
    roomFourFinished = false
    roomFourFrame = 0
    playdate.graphics.setBackgroundColor(gfx.kColorClear)
    roomFourAnimation = gfx.imagetable.new("Images/roomFourAnimation.gif")
    roomFourAnimation:getImage(1):draw(0, 0)
    local textBoxImage = gfx.image.new("Images/textBox.png")
    textBoxSprite = gfx.sprite.new( textBoxImage )
    endSound = playdate.sound.fileplayer.new("sound/pressA.mp3")
    crashSound = playdate.sound.fileplayer.new("sound/crash.mp3")
end

function roomFour()
    local roomFourtickCounter = playdate.getCrankTicks(4)
    -- every time a tick gets hit, add 1 to roomFourFrame
    if roomFourtickCounter == 1 and roomFourFrame < 30 then
        roomFourFrame = roomFourFrame + 1
        roomFourAnimation:getImage(roomFourFrame):draw(0, 0)
    elseif roomFourtickCounter == -1 and roomFourFrame > 1 then
        roomFourFrame = roomFourFrame - 1
        roomFourAnimation:getImage(roomFourFrame):draw(0, 0)
    end
    if roomFourFrame == 30 then
        roomFourFinished = true
    end
    if roomFourFinished then
        textBoxSprite:moveTo( textBoxX, textBoxY )
        textBoxSprite:add()
        local text1img = gfx.image.new("Images/text/roomFourText.png")
        text1 = gfx.sprite.new(text1img)
        text1:moveTo(textX,textY)
        text1:add()
        endSound:play()
        crashSound:play()
        if playdate.buttonIsPressed(playdate.kButtonA) then
            endSound:pause()
            crashSound:pause()
            fade(4000, "Images/fade/room5TitleScreen.png")
            state = "roomFive"
        end
    end

Horizontal.pdx.zip (1.4 MB)

Thank you!

Well, I got a simulator crash, and from a quick look at the output I'm guessing you're doing something in playdate.update() that you shouldn't be — possibly instantiating a FilePlayer, which you really don't want to do every frame. Hard to say without seeing the rest of the code, but does that give you any clue?

My playdate.update() function works by checking for the current "state" (literally just the room name is the only state i'm keeping track of, so I just hand the state variable the correct string when I wanna move to the next room), running the relevant xSetUp() function once (or so I think), and then running each main room() function every frame. This may give you more context without pasting all 700 lines here:


-- start the file (the entire game rather) with the "intro" state.
-- state gets set to roomOne at the end of the intro, roomTwo at the end of roomOne, and so on

local state = "intro"

-- other "call flag" variables above and below the roomFour one, all initiated as true, only set to false once they get run once in update()

roomFourCallFlag = true

function playdate.update()

  -- a few other identical If conditionals for the other room states above and below roomFour

    if (state == "roomFour") then
        if roomFourCallFlag == true then
            roomFourSetUp()
            roomFourCallFlag = false
        end
        roomFour()
    end

    gfx.sprite.update()

end

I'll just go ahead and attach the main.lua here as well. It's just a single file game.

main.zip (3.8 KB)

More on this regression here Fileplayer stops playing during performance load (Device only) - #5 by matt

My mp3s and code haven't changed but the problem started happening.

Yeah, there's definitely a bug in fileplayer. We're looking into it and I'll update this thread when we know more. Sorry about that!

2 Likes

I very much appreciate the attention. I'm happy to use a proposed workaround as well if it's something that might not be resolved for a while. Thank you!

Good news: it looks like we've already fixed this, and the fix should land in the next SDK update. Thanks again for the report!

2 Likes

woo! thank you so much!

Interestingly enough, 1.12.0 seems to have worsen the issue. No changes have been made in the code.

Have you tried a clean rebuild; this should be better, not worse. :slight_smile:

I’ve done so. I have only a single version of this project pushed up to itch.io, and someone else is seeing the same behavior as me as of 1.12, which I and others who have played it were not seeing to this extent prior. I’ll dig further and see if I can figure it out.

One of the changes in 1.12 was fixing fileplayer:getLength() for variable bitrate mp3s, but there's a bug there caused by the length estimator not rounding to whole samples. If the length value winds up with a fractional amount the fileplayer's outer loop says "we've got more data, let's go" then the inner loop says "nope, nothing to do here" and it spins. I'd thought that it would eventually get down to the decoder which would return an end-of-file code and cause the loop to end but nope. :frowning:

I've got a fix ready for 1.12.1, and in the mean time you should be able to get around the hang by using constant bitrate mp3s.