Music, Sound, Animation & Image auditioning app

I put together a little music file browser to enable me to audition digital audio music on the device. Here it is for all to use!

Some time later I added support for animations (GIFs or image tables).

And finally support for static images.

usage

  1. put your compatible files as follows:
    • MP3/WAV or .PDA compiled audio in Music/
    • GIFs or .PDT compiled imagetables in Animations/
    • still images or .PDI compiled images in Images/
  2. build and run in Simulator
  3. upload to Device

compiled files
files types such as (.PDA, .PDI, .PDT etc) can be put in the datastore directory, using the same folder names as above. That way you do not need to rebuild the app to see new files.

controls

  • Up/Down = prev/next
  • Left/Right = seek backward/forward
  • A = play
  • B = stop

menu options

  • seek speed (1,2,3,4,5) [music only]
  • auto next (Y/n)
  • loop play (Y/n)

notes

  • key repeat on up/down/left/right
  • currently playing song is marked with # and (MM:SS) time remaining, not in bold
  • playback position shown as a progress bar along the bottom of the screen
  • auto next independent of selected row
  • files sorted case-insensitive alphabetical
  • device auto lock disabled only when music is playing
  • settings saved to persist between launches
  • support for animations (GIFs or image tables)
    • plays at fixed frame rate
    • left/right to strep frames manually
    • up/down for prev/next file
  • support for static images
    • up/down for prev/next file

download

screenshots
audition-audio
(audio)

audition-anim
(animation)

10 Likes

It wont compile for some reason.

@Shadowdoom What's the compile/build error?

And did you put some music in the folder?

It just says that the corelibs don’t exist.

That implies your Playdate SDK isn't installed correctly.

1 Like

It’s fine, got it to work.

1 Like

I added support for previewing animations (GIFs or image tables)

Code in OP.

  • Added support for static images
  • Fixed bug in prev/next for non-audio files
  • Made sure things work OK from datastore directory
  • Ignores .DS_Store files

Download and code in OP

I am using Audition on my new device, it works great! I am using it to test the performance of the speaker. I really wish there was a way to drag audio files into the data folder without compiling into pda. Is there any way to speed up the process?

Glad you are finding it useful!

MP3 files don't need to be compiled, so you could use that format for testing purposes?

I seem to remember there was a feature request for the ability to read .wav or generate .pda on the device. Maybe?

1 Like

Have you ever run into issues with fileplayer streaming randomly stopping for no reason? Audition was working great when I had less than 8 PDA files or so (compressed IMA ADPCM). But as soon as I added more, the tracks will randomly stop, and the music:setFinishCallback(nextSong) will be called. They stop at a different place each time, which makes me think it is related to performance or garbage collection. I can't really see anything in the code that would cause this, so it might be a bug with the SDK.

EDIT: It might be related to the buffer size. Do you know what the default buffer size is?

I set the fileplayer buffer size to 1 second, which solves the problem. I guess performance hitches and garbage collection can interfere with the fileplayer if the buffer is too short. Unfortunately increasing the buffer makes seeking less responsive, but at least the audio will actually play. Do you think this is a bug I should report?

Definitely report it.

I know there were some changes to audio playback in the past due to the stream cutting out.

Right, the problem is the fileplayer is starving for data because the decoder can't keep the buffer full. Using a bigger buffer gives the decoder more leeway to use CPU when it's available. The default size is 1/4 second, but I think I'll bump that up to 1/2 s because other people are running into this.

The other thing going on here is that I decided that fileplayers should stop playing when they run out of data because I hate it when the audio starts stuttering in a game. (Probably because I suffered this a lot as a kid, trying to play games that my ancient PC wasn't up to.) You can do fileplayer:setStopOnUnderrun(false) to have it stutter instead. Another weird thing/bad decision here is the flag is off by default in the simulator--that is, files will continue and stutter--but on on the device. I turned it off there because the simulator decodes on the main thread and macOS will happily block the main thread for long enough to drain fileplayer buffers, just from e.g. clicking on a menu. I should move that to a separate thread to match how it now works on the device.

When you say you started seeing the problem when you got up to around 8 files.. Are those all playing at the same time? Or just in the file list? I'm not surprised if it can't keep up playing a bunch of files at the same time, but if those extra files are just sitting there I don't know why they'd be slowing things down.

One last bit of history here, for the record: When we changed to a journaling filesystem driver on the device we unintentionally added a limit to the number of simultaneous open files. Some games were creating fileplayers at startup for every file they might play, and each was keeping an open filehandle for each. Eventually the system can't open any more files, and the game crashes. In 1.10 I changed it so the fileplayer doesn't open the file until it starts playing or you manually set the buffer size with fileplayer:setBufferSize(). But ideally you'd only have as many fileplayers as you need to play simultaneously, and use fileplayer:load() to change what they play.

1 Like

Nice app, matt! I'm digging the bare-bones design.

1 Like

Thanks @nanobot567 !

There's only ever one file playing at a time here, but lots in the list.
List populated with listFiles
On my device I have ~50 files (2xMP3, 1xPDA, 3xPDI, the rest PDT)
fileplayer.new is called on press of the A button.

Do I need to discard the fileplayer after use? I don't think I am doing that right now.

If you're letting it scope out it should get collected and the resources freed up. I'll double-check that the file is actually getting closed and not leaked, tho. I'm not sure what's going on with that buggy mp3 file. It spews a bunch of "bad loadlen" errors and then the hang is a deadlock on the audio device. Looks like the error is causing us to jump out of a function without releasing that lock.

Thanks for clearing that up. I didn't know about the fileplayer:setStopOnUnderrun(), but that would address the problem I was having. I think I would always prefer stuttering to the file completely stopping, as it gives me an idea of how the game is performing and where I can improve things. To answer your question about the files, as far as I understand there is only ever one file being played at a time. I'm guessing adding more files creates a bigger load due to the text being drawn in the update function, as well as more frequent garbage collection. I think it's mostly the garbage collection spikes that cause the issues with the short buffer size.

1 Like

This is an awesome tool and great for prototyping! It also reminded me that it would be useful to be able to modify file playback speed in the SDK without affecting the tempo (which is what fileplayer:setRate() does). I'm not sure how those complex those algorithms are or if the hardware/OS would even support that, and maybe @dave would know. Having a tempo-only option would allow an app like this one to play back podcasts and such at 1.25x or 1.5x speed, or enable the creation of slow-mo/running-out-of-time game effects by dynamically speeding up (or slowing down) the background music without having to export multiple versions of the same song.

1 Like

Time stretching is doable but not simple. One of the games uses GitHub - dbry/audio-stretch: TDHS (time domain harmonic scaling) library with command-line demo and it works reasonably well. We're pretty tight on firmware space, though, so I don't see us adding anything like that to the core runtime.