Testing

Progress

03 Jul 2007

JonathanGordon and I have been working on implementing the BufferingAPIProposal. This API aims to provide a simple interface to an underlying buffering system, so that the playback code doesn't need to take care of the buffering itself as it currently does. This will (hopefully) result in a simplification of the playback code.

We have the API working, but only in an immediate manner. This means that the API calls have to be made at the right times, whereas the buffering code should queue the calls and decide itself when to treat them.

Our code works as a standalone application that accepts a list of files as arguments. It will buffer them and then simulate their being read by outputting them. The files we get as output should of course be exactly identical as the input files, and we have achieved this.

09 Jul 2007

There is now a test plugin which includes some threading. The API calls have all been implemented and tested (see BufferingAPIProposal). The plugin simulates a playback thread issuing API requests to the buffering thread. Playback is actually the action of writing files to the disk. The files get outputted correctly, both in the simulator and on my Gigabeat. Currently there is only one file in the buffer at a time.

The next steps are the following:

allow several files in the buffer simultaneously (should be quite easy)

implement the link between an audio file and its metadata (I have a general idea of how this will work)

tie the code in the current playback engine (this may or may not prove complicated)

17 Jul 2007

The plugin now has a preliminary implementation of the Ring-Buffer-in-the-Ring-Buffer™ concept. Each individual handle data buffer is now potentially a ring buffer of its own. This capability is used for files that are too long to fit in the buffer and will (hopefully) make it easy to have several files on the buffer.

A picture can help explain this :

Here the main buffer is represented by the line. There is only one data handle in it, and it wraps around the end (which is why its end is before its start).
The handle's data buffer is full and has wrapped, the current reading position being white and the writing position being black. In most cases, the start of the file will simply be at the start of the handle data buffer, but in this example we assume a part of the file has been read, allowing for more of it to be buffered.

Implementing this is the first step towards having several files on the buffer while allowing to make sure the metadata stays in place as long as required. It turned out to be more complicated than I originally thought, but now it is in place, the next steps should be easier.

06 Aug 2007

I've been able to work a bit on this during my holiday (which isn't quite finished yet). I've decided to drop the RingBuffer-in-RingBuffer idea (based on IRC discussion that happened around between the 17th and the 20th of July IIRC) and instead go back to the previous, simpler implementation. I've implemented most of the actual MoB in the test plugin, with metadata moving. My work is available in a Git repository I published here (the interesting branch is no_rb_in_rb).

14 Aug 2007

The test plugin (testplugin.c) is now able to get the real ID3 metadata from the files it is given, instead of reading associated text files. It successfully moves this metadata in the buffer to save space as the audio is read and freed and makes it available to read. I have started immersing myself into playback.c to see what needs to be tweaked and/or rewritten (the r14326 change is the first step).

I have also rearranged my public git repository slightly. master is renamed to rb_in_rb and no_rb_in_rb becomes the new master branch.

16 Aug 2007

I have started to implement the MoB concepts in the core code of Rockbox. My work is available in the mob branch of my public rockbox git repo. I will have little or no more time to work on this before the 20th (the "pencils down" date for the GSoC evaluations) because I will be on vacation between the 18th and the 25th, but I obviously intend on finishing the implementation ASAP after that (and my September exams).

02 Oct 2007

I have made good progress in making the core playback engine use the buffering API. Current state is that the first audio track plays fine. Buffering the following ones is still disabled but being worked on.

The web interface to the public git repo allows viewing the diff between the master and mob branches.

10 Oct 2007

Progress is good. It is now possible to listen to a whole playlist (of the same codec for now... codec transitions aren't always correct, but I haven't looked at them at all) without problems, with seeking (even beyond or before buffered data) and skipping forward (backwards isn't done). Stopping and resuming function perfectly too. Music starts as fast as in the SVN code, metadata is displayed without any problems.

13 Oct 2007

We're approaching committable state! Codec change works, as well as skipping backwards and forwards. Directory skipping works in both directions too. I added a debug screen so that it's possible to see what's happening. Using this debug screen, I optimised the buffering strategy to reduce disk spinups. Work is still needed to correctly support playlist reordering, and it could be good to be more conservative with bufclose calls and keep data in the buffer a bit longer (this requires adding state information to the memory handles).

Technical details

The buffering thread is responsible for the management of the buffer space. It tries to maximise the amount of data available for the other threads by filling the buffer at appropriate times
(i.e. when the disk is spinning) and freeing data which isn't useful any more.

The buffered files are managed by a linked list of "handles", which contain all the necessary information to track the status of the files. The handles are found and managed using their ID, which is a unique identifier.

Currently the linked list implementation I use is a simple one I wrote myself. However ChristianGmeiner, who is doing the UsbSoftwareStack project, also uses a linked list and he chose the implementation from the Linux kernel. The plan is to make that implementation available in the Rockbox core and to port the code to that implementation. We could also port an existing custom linked list implementation to that one.

Functions documentation

Here is some of the documentation I wrote in the main source file, about the important functions :

LINKED LIST MANAGEMENT
======================
add_handle : Add a handle to the list
rm_handle : Remove a handle from the list
find_handle : Get a handle pointer from an ID
move_handle : Move a handle in the buffer (with or without its data)
These functions only handle the linked list structure. They don't touch the
contents of the struct memory_handle headers. They also change the buf_*idx
pointers when necessary and manage the handle IDs.
The first and current (== last) handle are kept track of.
A new handle is added at buf_widx and becomes the current one.
buf_widx always points to the current writing position for the current handle
buf_ridx always points to the location of the first handle.
buf_ridx == buf_widx means the buffer is empty.
BUFFER SPACE MANAGEMENT
=======================
buffer_handle : Buffer data for a handle
free_buffer : Free buffer space by moving a handle
fill_buffer : Call buffer_handle for all handles that have data to buffer
can_add_handle : Indicate whether it's safe to add a handle.
data_rem : Total amount of data needing to be buffered
wasted_space : Total amount of space available for freeing
These functions are used by the buffering thread to manage buffer space.
BUFFERING API FUNCTIONS
=======================
bufopen : Request the opening of a new handle for a file
bufalloc : Open a new handle for data other than a file.
bufclose : Close an open handle
bufseek : Set the read pointer in a handle
bufadvance : Move the read pointer in a handle
bufread : Copy data from a handle into a given buffer
bufgetdata : Give a pointer to the handle's data
These functions are exported, to allow interaction with the buffer.
They take care of the content of the structs, and rely on the linked list
management functions for all the actual handle management work.