MPlay3 - MP3 Music Player»Blog

First developement update. One month on handmade network.

Hi, Tim here.
It is a bit over a month now since the submission for this hobby project was accepted on here.
Now it is time to give a small update on what I have been doing. To be able to give proper updates I started a progress file where I documented all the smaller and bigger things I worked on and found worthy of writing down. I put the full file at the bottom of this post.
Besides many small adjustments to the usage of the application and many bug fixes, the main things I did were implementing the ability to create custom color palettes (including a color picker) for the user, sorting out the renderer (literally), and switching the basic memory management from a very simple bucket-style allocator to a arena/zone allocator and enabling the playing of arbitrary .mp3 file sizes (up to 4 GB).
I also added an application-quit animation which I find neat. To quit the app you can press escape and a) hold it for a short time, or b) double-tap it to quick-close the program. Alt+F4 does the same as the double-tap. The closing is accompanied by a "curtain" lowering which is the animation part...

Some screenshots of the mentioned features:

Color Picker:

Custom Palette Pink Dreams:

Demonstration of large file:

Quit Animation:

Here is the whole list:

********* Progress Info - DevLog *********

- Fixed shuffle not effecting playing songs, when it was already in playmode.
- Fixed Assert crash when there are more samples to be played than I write every frame (happened on output hardware change).
- Fixed DisplayCursor not being updated in some situations.
- Cleaned up updating the sliders. Previously you had to always do it manually, which was error prone. Now the function that changes column positions itself handles the slider updates.
- Fixed bringDisplayEntryOnScreen stuff for search. How did that ever work?! Well now it does.

- Large conversion of file/batch/playlist/displayable arrays to use their own id structs to make it _much_ clearer what is actually happening. This was long overdue for making the code easier to understanding!

- Fixed bug where you could not properly select new song when a _up next_ song was playing.
- Fixed bug where the end of the song is sometimes cut off.
- Fixed bug where playing song was erroneously evicted and another song started playing in its place.

- Fixed color not set, when selecting song and no song was playing
- Added DebugTimer feature, to time code blocks via StartTimer() and SnapTimer().
- Added double click for all columns except for song. Double click now selects all slots that are displayed.
- Added double click for song column. It starts playing the clicked song or pauses it, when it was already playing.

- Changed QuickSort3 in FillDisplayables to a much simpler RemoveCheckValue, as they actually do not need to be sorted at that point.
- Added a quit animation when pressing Esc. Now it does not just quit, instead you have to hold Esc for 1 second.
- Added quit animation also to alt+f4 and window X button with double speed.

- Added error messaging for the user. Only for main thead yet.
- Added a error message queue for threading.

- Added double-escape-press for fast/auto quit.
- Added a "wait for double-escape-press" in animation.
- When a .mp3 decode/read error is detected and pushed to the user, now it will not crash if the song is pressed again.
- Finally added ReadEndOfFile to extract ID3v1 metadata without reading the entire file.
- Now properly searching for additional metadata in ID3v1, if ID3v2 or ID3v3 do not have all infos.
- Special keyboard key for PlayPause/Stop/Next/Previous are now processed even when application is not focused.

- Fixed bug: Selecting song through search was crashing. Needed to call other procedure based on column type.
- Fixed bug: Making window bigger while a column is at the bottom repeated the last slot for the new incoming ones. Fixed by updating the column to look for overshooting the end and stop when hitting it.
- Fixed bug: Fixed array out of bounds assert made visible by fixing previous bug. Calculation of NewCursorID in ScrollDisplayColumn could be one too high.
- Started ColorPicker. Created color texture generation with blackness level.

- Added local_string_compound to string api
- Added custom color palettes read and saved to settings file.
- Added custom color palette functionality, switching through them with F11.

- Almost finished Color Picker basic setup, with color spectrum and picking.

- Worked more on color picker. Most things already work, like...
- selecting different colors for specific palettes
- overwriting existing custom palettes
- opening closing color picker

- Finished the color picker, minus some user feedback stuff.
- Added palette name display, which is editable for the custom ones.
- Added remove button, to delete custom palettes.
- Moved quit_animation to ui-api, but not yet adapted properly.

- Added feedback for pressing the color picker buttons.
- Fixed a bug with determining the proper color for the picker in RGB to HSV conversion (H fell out of the 0-360 range).

- Started converting from _load and decode entery file_ to decode only some mp3 frames at a time.

- Implemented an arena allocator to switch to. As the original bucket allocator is too simple for my current needs.

- Changed arena allocator quite a bit. Now it is simpler again, with no additional transient memory.
- Switched away from old bucket allocator to the new arena allocator. Average memory footprint is now ~1/4th.

- Created new files for SoundJob procedures
- Created new files for SoundSerialization procedures
- Removed unnesessary dependencies in Renderer.c
- Switched all remaining .cpp files to .c files. Just for a bit more consistency. Maybe I should do it the other was round...
- Renamed Sound_Backend_TD.* to Sound_Thread_TD.*, to fit more what it actually contains.
- Renamed Sound_TD.* to Sound_Backend_TD.*, as this name fits better to those files and they needed a suffix, as it was annoying to switch to them.
- Macro cleanup

- Switched to a two stage .mp3 decoding procedure. Now it only preloads 5 seconds and only starts to fully load the one file that is selected for playing.

- Reduced memory footprint by sizing the buffer for the loaded song properly. Should now be able to play .mp3 files of any size (smaller than 4GB)

- Fixed bug with trying to read first .mp3 frame and not having the right metadata size (seems like faulty id3 tag data). Now it iteratively loads more of the file if the initial size was not enough until it can extract the first frame.

- Implemented proper decode canceling for the full file decode. If the user skips through songs faster than decode speed. The decoding is now canceled and the new one is started immidiately after.
- Fixed song skip if decoding of file takes longer than preload time of currently 5 seconds. This is done by using the partially decoded song, up to the point it currently is decoded.

- Fixed time overflow at the end of song.
- Added drag freezing for timeline slider, while song is not loaded.

Simon Anciaux,

I downloaded the code and build from github and I'm running in some issues (both with the git hub build and my build) on Windows 7.

- The git hub repository contains you library data, and that causes an error message when I launch the application if I don't remove the files.
- It seems like the assets are not found. I'm not sure what the working directory should be. I tried launching from the data folder but it doesn't seems to change anything. It could also be that the draw order isn't correct since the timebar handle seem to be drawn behind the bar.
- Some letters are not displayed correctly.

- The text in the field to specify the folder to scan isn't visible.
- When I try to scan my whole music folder it crashes in Sound_Backend_TD.c, on line 831, on the assert that verify the metadata year is less than 10000. The year is value is 4294961968 ( 0xffffeb30) and came from the path were Year.Pos != 0 (Year.Pos is 1). The string Year.S contains "0". But on that line you don't use the length of the year to parse the value, you always use 4 characters. Since your CharToU32 function doesn't range check the character, you can end up returning negative numbers. Also in the function ConvertU32FromString, you use a i32 but return u32.
- It's running at 300fps, using one of my CPU thread at 100%.

- There is a issue related to AMD graphics driver, that makes the window viewport not aligned correctly in the window (well that's what I think is happening), making part of the window not draw correctly. It's a bug in the AMD graphics driver, so you might not want to fix it (it's Windows 7 only I think) but a simple fix I found was to force a resize of the window after it's creation but before displaying it. For example I create the window with it's width - 1 and than call SetWindowPos with the correct width before calling ShowWindow.

SetWindowPos( window_handle, 0, 0, 0, width, height, SWP_NOMOVE );
Tim Dierks,
thanks for the in-depth feedback and listing of the issues you had and for trying out my application!
I think you may actually be the first person, other than me, to try using the progam. At least the first I know of.

To your issues:
- The working directory should be the "build" folder, and from the screenshot it seems that the data folder is found. Because the font is in there as well. The missing buttons and stuff is most likely the sorting issue.
- The display artifacts of the letters, the missing button icons and the not visible text in the scan screen are also because of that sorting bug. Which I try to sort out. I will check on other machines if I can replicate that issue.
- For the existing library file I implemented a proper check if the music folder actually exists and if not, it should just work as if the library file did not exist and ask you to input a new location.
- I fixed the Assert crash for the year. Should hopefully work, but i'm optimistic. But especially the metadata stuff is super janky and other errors might come up. I hoped to get most weird edge cases with my 8k .mp3 files I test on, but I was not lucky.^^
- I implemented proper fixed framerate of 60 FPS. This was not a problem for me until now as normally for windowed Windows applications the VSYNC is always activated. Could be that it is deactivated in your driver, but that shouldn't matter anymore.
- I also added the SetWindowPos call for the driver-bug fix you posted. But I could not test if it actually works.

I will post an update when I fix the sorting issue, which makes the app not really usable...

Thanks again,
Tim D.
Abner Coimbre,
Simon strikes again :D

Good luck with MPlay3 Tim. I'm definitely old-school; I remember transferring local music to my PSP handheld and how much of a joy that was. Hopefully I can try your thing once the main issues are ironed out.
Simon Anciaux,
- I implemented proper fixed framerate of 60 FPS. This was not a problem for me until now as normally for windowed Windows applications the VSYNC is always activated. Could be that it is deactivated in your driver, but that shouldn't matter anymore.

For applications, you should try to only update when necessary. There isn't a reason why the application needs to refresh the whole window at 60fps. You can use GetMessage instead of PeekMessage so that the application sleeps when there are no message, and once it wakes up, use PeekMessage to retrieve all available messages. Since you still want to update the time and time bar, you still probably want to refresh at least every seconds. You can probably use a timer for that. One step further would be to only refresh the part that changed (but that might not be worth it at all).

For some reason vsync was set to off by default in my driver. I must have tested something and forgot to set it back to on by default.