A. General considerations


The general prebuffer, that still works for external mpg123 (or whatever) process.
Or not. I have limited time... I want a prebuffer with zeroscanning and for applying various filters, but I do not need the external mpg123 process anymore since there is libmpg123 now, also, what other decoder is there that I would use that way? You can imagine web streams... mpg123 could play those.
But, it is messy and complicated, that 3-way piping stuff. Was fun to sort it out and get it working, but libmpg123 offers cleaner work with less of my lifetime wasted. Heck, let's just port the prebuffer over, to get silence filtering and preprocessing for all inputs.

Now, I will drop that asynchronous stuff. Keep is as simple as needed.
I can always dig out the old code from SVN.

Now, how shall it work, properly?

I need to separate the position in the prebuffer and the prosition in the input file. The mpg123 input did that, right?
Well, it handled it to some extend, for a seek checking if it is enough to skip some samples in the buffer. But data->position always indicated the position of the first unread sample in the buffer.

With the buffer being a separate entity, it needs to separate its position from the input file position. And then, there is the adjusted position for the outside, after applying stuff like zeroskip... or some resampling fun.

I also need to clean up the data handling. Having one lump of input_data for everyone around the hierarchy is not good. That needs structure.
What does the input file really need to know?

- private data, library handles and such
- explicit parameters for function calls like seek/eq/open ...

The filename given on open may need to be reused, in that case the input file has to cache it itself. There is not so much data to share at all!


The positions we have:

1. Position in input file. The simple offset from the beginning of the file.
2. Position in prebuffer. 
   That's (filepos - prebuffer.fill).
3. Overall position, taking zeroscan offset into account.
   That's (filepos - prebuffer.fill - prebuffer.zerooffset).

One note on scan_length: That can be cheap when not considering zeroscan.
But _with_ zeroscan, we either need to accept some inaccuracy or we need to decode at the beginning until zerolevel is exceeded, then seek to the supposed end minus the maximum zeroscan length and zeroscan that piece.

Also, the work of converting the input format to the mixer format should be done inside the prebuffer, offloading as much of the work out of the central mixer loop as possible. But here it may be enough to do the resampling / amplification step during the parallel extraction of data from the buffer (including effects processing), for now, at least.


B. The tricky thread control / synchronization.

I got one FIFO buffer with different reading and writing threads. I do not deal with general atomic operations on read/writeindex and buffer fill, I use explicit thread synchronization via semaphores.
Well, I should do this consistently, actually.

The old way probes the current buffer state variables to see if the fifo_reader added something. That is problematic, as there is no guarantee that the bytes of these variables are in a consitent state. I should only read these variables when in there is noone possibly writing to them. 
Also, I really want to be able to control fifo_reader, to stop it, to start it.

The world of C offers two tools: Semaphores and locks. Both can do about the same... but are different in convenience.
When I want to access the buffer properties I need to ensure that the fifo_reader does not mess around right then. That sounds like a lock around the buffer.
But also, it means that the outside cannot easily use the prebuffer as a generic buffer. Hm, without taking a lock, that is.
There is some dirt to clean up with my whole half-understanding of volatile variables and the issue of (non-)atomic operations. But first, I need to make that fifo_reader control foolproof.

Currently, there is a deadlock, that appears just about sometimes. I need to understand why / I need to fix the communication.
Actually, I insist on understanding it.

What's happening? The fifo_reader is an endless loop, that

1. posts on a semaphore (wsem), indicating that it's waiting for commands
2. waits for a semaphore (rsem)
3. sets both semaphores to zero
3a. goes back to the beginning if the flag for pausing is set
4. does work, posting on dsem when having added  new data to buffer or encountered the end.

The other side is possibly less organized. It tries to ensure a stopped state by

1. setting the pausing flag
2. posting on rsem
3. waiting for wsem
4. unsetting the flag

It tries to ensure a running state by
1. posting on rsem

I assume that stop() and start() are not running in parallel. But I do not assume that calls to stop() ans start() match up in count.

Caught in between is the code that wants to wait for a specific buffer fill or end. It loops over

1. check if there is enough data / end
2. wait on dsem.

Now this encounters a deadlock .... because of the non-atomicity of the operation on the fill variable, I suppose. Not totally sure, but there is a window that needs to be closed. We need to be safe with the synchronization.

Does the start/stop part work properly? The flag should be tranported fine because the semaphore is posted after that operation.
There may be complications when a third thread interferes, firing semaphores in between. But, well, let's exclude that. There is one commanding thread.
Assuming there is no third thread, how safe is the stop() operation?

There may be a reading cycle, plus some zeroscanning, until the fifo_reader sees the pausing flag. Oh, for the pausing flag...
That one is set usually while the fifo_reader is working somewhere. Possibly during the fifo reader checking it. The setting of that flag is non-atomic.
Gah!
Well, let's place a lock around that flag and the buffer. That's an overly locky FIFO, then.


C. The zeroscan computations.

I tried to separate out the FIFO read/write index stuff as well as the actual zero scanning into the fifo_buffer class. That still misses the integration of zeroskip stuff into reading and seeking. The position computations need to go into the buffer... Especially, It should only offer the data that can be safely delivered without endangering the end zeroscan (so only give everything when the end is already there).
