Commit Briefs
consume all enqueued messages before calling imsg_read
player_dispatch reads only one imsg from the ibuf. Next time it's called, the other messages on the ibuf (if any) are discarded and new ones are read. This can cause the player process to go out-of-sync with the main process if multiple messages were "bundled" in the same chunk. To avoid this, always try to imsg_read before. If it succeedes, we read process the equeued messages one by one, if it fails then we poll for new data and call imsg_read to process them and retry. This fixes a bug where amused could be "confused" by running $ amused pause ; amused stop ; amused play in a loop a few times. This bug and the repro were reported two months ago by Dirk-Wilhelm Peters, thanks! (and sorry it took so long to understand and fix the issue)
don't set the imsg fd as blocking mode
just do a poll before imsg_read in player_disptach to wait for data to read. It's not performance-critical code, so this is fine. If it were important not to do an extra poll(2), for example when we're called is called after player_pendingimsg we already know there's something to read, we could move the polling in the case `n == 0' below.
don't send the song' path to the player process
we're not relying anymore on the file extension, so this information is useless for the player.
don't call player_sendeof on IMSG_STOP
the refactoring introduced this error where we call report an EOF upon IMSG_STOP, making the player infinitely loop.
add a simple filetype detector instead of relying on file extension
just a bit of "magic" :) Flac are easy, they always start with "fLaC". mp3 are weird because they either start with "ID3" (but this theoretically only ensures that's a tagged file, not an mp3) or 0xFF 0xFB. Ogg Opus should have a magic sequence "OpusHead" somewhere near the start of the file but also have the ogg' "OggS" magic bytes. I hope it's enough to distinguish between Ogg Opus and Vorbis. Another option would be to refactor play_oggvorbis/opus to not close the file on failure and try in cascade the play_* functions, but it's more complex and this solution seems to be enough.
refactor player_playnext: return status code
let the caller decide to call senderr or sendeof.
pledge audio too
while for my particular use-case this has always worked with only "stdio recvfd", sio_open(3) mentions that if no further sio_open calls are made all pledges can be dropped except for "audio", so let's keep it.
remove the got_stop hack
now player_playnext has enough information to decide wheter to call player_senderr or player_sendeof.
change the log debug string for player_setup
now it includes all the params, not only the rate