/*
for(inputs)
{
	in.fill_buffer()
	(send to sleep?)
}

for(outputs)
{
	mix out.inbufs to out.buffer
	(wake output here..)
}
(...or here?)

(wake inputs?)
wait for outputs
					
*/
		
/*
big problem: processing of commands / changes for inputs

basic audio processing/conversion in input object or not?
-> it is nice for the mixer to have the defined global audio format from the inbuffers, but it's a hell for the inputs to provide that
-> on the other hand, the mixer would always have to tell the inputs how many of their samples to read

Variables:
	sample_freq, pitch, channels (mono/stereo) -> number of raw numbers to deliver
	bitrate -> how many bytes make a "number" .... how many bytes to read
	
	then: conversion of the data... 8bit->16bit; 16bit->8bit
	looks like old mixplayd... 
	I should have a look at what it does on this... It didn't look to me like it is prepared for different audio formats.
	

So, mixer says

in.copy_buffer() // this gives audio directly or waits for the prebuffer thread to finish and swaps with buffer

if channel should live on:
	in.prepare_buffer(number_of_samples) //this can activate a thread on decoder stream or just remember the value when fill_buffer does the real work
if channel shall die: make it disappear after mixing

...variable buffer types in input (16/8bit)?

kann mich spter noch entscheiden: statischer Speicher mit unterschiedlichen Zeigern oder gleich dynamisch... nderungen sind nicht so schlimm

Big problem again: when there are not BUFFER_SIZE samples from input (either too slow or at end - end more likely)

what mixplayd does: just pad with zeros!!! Works somehow if decoder not too slow. Bad for gapless playback.

What does mixplayd on pitch?
	temp buffer of 40*BUFFER_SIZE
	read the really needed number of samples
	convert to real inbuf
	
Bad Ass! It cheats!
What do I?
	tell how many samples I want in advance (prepare_buffer(number)); this reallocates (if necessary) memory for the prebuffer and starts reader on that
	get a pointer to that buffer (get_buffer())
	convert to real inbuffer 
	
	But: what with gapless or too slow decoder?

	When I only got x samples from on of the decoders, I can only play x samples and preserve the rest(?)
	I won't deal with that now...
	But gapless: 
		special mode of two channels together, they share one output buffer. the following channel uses special load command (append <ch> <file> <ch to append to>
	 don' forget: mpg123 object may have to delete unnecessary silence (padding) on end!
	 


Normal operation:

while(active)
{


Stage 1: administration

lock channel comm queue

[do commands collected by communicator / script commans pending]

Stage 1a: channel stuff for next piece of audio
[
		p.ex. adding/deleting of channels, remap bindings between channels and outputs
		volume, pitch, eq settings,	loading of tracks, seek, ...
]

unlock channel comm queue

Stage 2: Collect data

---going extremely massive parallel: inputs working

Stage 2a: send requests

for(inputs)
	//number of samples given here (per channel)!
	if(in.playing) in.prepare_buffer((int) in.speed*BUFFER_SAMPLES/format.rate*in.format.rate)

---as time goes by...

Stage 4b: wait for output being done

for(i=0; i < output_num; ++i) sem_wait(output_sem)

---output halted

Stage 1b: output stuff

lock output comm queue
[
	add/delete, redirect to other devices, suspend...
]
unlock output comm queue

Stage 2b: wait for inputs

for(i=0; i < input_num; ++i) sem_wait(input_sem)

---end of parallel part

---this now occupying the cpu fully...

Stage 3: mixing
[here we have to think about all the complicated stuff: padding, gaplessing, or ignoring...]

for(inputs)
{
	if(in.playing)
	{
		[take inbuf, convert, ]
		padding or if(in.append)
		{
			[somehow get the cached data of the channel to append and activate it for following playback, sort the buffer out to get the next read right, copy relevant properties (notably connected output devices) and remove them from the ending input]
		}
		[mix to output buffers (when corresponding device has been deleted, the remove it from channel's list here)]
		[--> have_data = true when at least one output channel got sth (have option to insert silence when nothing is played... one may want some pause)]
	}
}

Stage 4: output (if something there to put out)

---start of massive parallel part: outputs working

Stage 4a: activate outputs

if(have_data) for(outputs) out.play()

}


data format for actions with arbitary parameters: 


THE TARGET DILEMMA

Conclude that there are four kinds of commands:

1. Overall mixplayd commands: "I'm sick of you: Die!", "Wait a minute!"
2. concerning input: new, delete, load, play, pause, volume, eq, ...
3. concerning output: new, delete, change device (really?) (, ...)
4. concerning both: bind

2 and 3 could be done while the other part is active, respectively
1 and 4 could be grouped in the mixing stage where both parts are sleeping.

How much sense does the complication make? ... Is it really that good to always have one piece of input ready when the global pause ends (the next piece could have difficulties catching up - re-awakening of threads (unlikely) and child processes  - even worse: our nfs server spinning up the discs again...)

No. Keep it simple:

while(active) #while(true) is the same here...
{
	Stage 1: administration 
	if(!active) break;
	Stage 2: getting input (parallel) 
	Stage 3: mixing
	Stage 4: feeding output (parallel)
	
}

The communication threads are free to set the actions for admin during Stages 2 - 3

Network stuff:

a "mother" comserver in an endless loop that consists of a _blocking_ accept() call on the main socket (communication port)

on successful accept(): take file descriptor or streams (somehow I'd prefer an input and an output stream for simplicity...) and start new thread workig on it (them)

the "child" threads make a blocking read()

[in between: made two classes out of mixplayd's netcomm]

What happens when a connection gets closed?

comthread: ...create an action and pthread_exit... 

...main chief joins... or not...

Hm.

What about port watcher just starting all the threads, filling threadlist, and the threads killing themselves on "close" or "shutdown", removing themselves from threadlist.



Again and again:

Who does what when concerning commands?

Socketeer getting string. Parsing: valid -> add it to actions, wait for semaphore
these actions are triggered or performed by main worker; semaphore gets touched accordingly
...socketeer awakes and gives response to socket... but what about success or errors? Need variable on that (string)... internal to socketeer... put pointer in action...


Gapless:

the daemon has to know that a track is to be played adjacent to another ... 
two possibilities:
 - there are n samples of first track, m samples of next one have to follow to fill the m+n samples in buffer
 - first track goes exactly to buffer end; next track just has to be set on PLAYING for next turn

Both tracks needn't to have the same sampling freq, speed, volume... 

so... play() on all PLAYING inners

wait...

fetch data and add the frames one has gotten, always.
see if track is at end; mark it as stopped (better ENDED?) - look if there is a track to follow
if not: do nothing, procceed with the other playing inchannels
if indeed so: mark next track PLAYING, fetch data from it to fill the remaining buffer (even if it is already filled... just order 0 samples... one could check here before, of course


Problem: There is serial waiting. Solution: decoder automatically prebuffers on load(). On first play(), it feeds the data from this buffer; if not enough there, read; if too much: leave rest in prebuffer, gradually empty it on next play()s

The only solution I see here as I have to stumble on the other track's end to really know that it is the end, then knowing how much data I need (additionally, speed and... wait another prob: speed change after load() doesn't break the buffer, but eq settings do with mpg123; one can check... but for gapless I can assume that eq settings are the same as for the previous track.

another possibility could be to have a channel state BUFFERING (resulting to BUFFERED when done) that lets main do the buffering on the follow ch0 ch1 (ch1 follows ch0 without gap) command; re-doing the buffering on eq or speed changes (always trying to get one expected output buffer fill) 

That seems to be the better idea. Yep. Keep the decoder simple.

So, now every load() makes the input channel BUFFERING;

Any command that invalidates prebuffered audio triggers BUFFERING on BUFFERED channeln

the playing loop now triggers play() for PLAYING and BUFFERING channels
	-> BUFFERING: always order the full package to fill the output buffer
	-> PLAYING: get what is not already in buffer

the mixing loop now handles two states:

BUFFERING: read and compute the device buffer into the channel buffer, set to BUFFERED

PLAYING: read and compute the device buffer into the channel buffer (adding to it if there is still some fill) and add to all output device buffers.

(this means opimization for more than one output device bound to the channel...

otherwise: 

BUFFERING: read and compute in ch buf, BUFFERED

PLAYING: empty channel buffer, compute from decoder directly to output buffers (including recalculation for every output).

some words about status. I suspect that BUFFERED is obsolete

an input channel can be:

PLAYING or BUFFERING 
PAUSED (BUFFERING?)
STOPPED (BUFFERING?)
SEEKING?... look to the future --- multithreaded seeking needs a lowered priority for the seeker so that it doesn't disturb playback

BUFFERING is no state for external users... at least not of the same class as STOPPED; worse: it can be PAUSED at the same time
BUFFERED makes no sense for it self; one can check buffer_fill - has to, anyway.
-> one bool in class data

On all buffers: what does buffer_fill mean? Number of audio_types or samples? For non-stereo playback I need this decision.
Samples.


Works quite nice...

Timing: I want to leave seeks on non-playing titles (or even them?) running in the background over multiple cycles. They can consume a great deal of time. But on buffer invalidation I need the position _now_ (do I?)


Problem beim Beenden... Konnte das gerade nicht nachvollziehen:

[socketeer] Got message with 8 characters... namely: shutdown
[socketeer] waiting...
executing actions
[main] action loop
[main] executing action type 202
[main] actions done
[main] going berserk...
[socketeer] cleaning...
[main] cancelled port watcher...
[main] would like to delete 0x8062aa0
dummy_input: goodbye
Speicherzugriffsfehler


tritt selten auf, manchmal aber schon.

anderes aus der standardsituation:

[socketeer] waiting done
[socketeer] Got message with 8 characters... namely: play 0 z
[socketeer] waiting...
executing actions
[main] action loop
[main] executing action type 3
[main] wanna play file z start channel 0
[main] new indevice created on channel0
dummy_input: loading file z[main] loaded file z on channel 0
[main] actions done
[main] ordering 1024 samples for playback
dummy_input: filling buffer with 1024samples
[socketeer] waiting done
Speicherzugriffsfehler

Tritt auch selten auf. Ich muss den volatile Mist klren...

Auch bei play 0 er; load 1 t; follow 0 1:

executing actions
[main] action loop
[main] executing action type 30
[main] actions done
[main] ordering 1024 samples for playback
dummy_input: filling buffer with 1024samples
FILL: 1024 with channelcount of 2
filled
[main] ordering 1024 samples for buffering
dummy_input: filling buffer with 1024samples
[socketeer] waiting done
Speicherzugriffsfehler

Habe den succer auf volatile gemnzt... evtl. hat das geholfen.



Nun aber: Erster Versuch mit multithreaded mpg123_input... ganz bse; entweder gleich nach spawn gettet oder sowas:

Nach den Klausuren, in den Ferien ... mal zwei Tage durchgemacht. Irgendwie ist der code da, muss nur arg aufgerumt werden.
Stabil muss sein. Dann gapless.

[socketeer] Got message with 7 characters... namely: start 0
[socketeer] waiting...
executing actions
[main] action loop
[main] executing action type 6
[main] wanna start channel 0
[main] status now 1 (playing)
[main] actions done
[main] ordering 1152 samples for playback
[socketeer] waiting done
[mpg123_input] fetching 1152 samples
[mpg123_input] reallocated memory
got 1152 samples from decoder
mixing
Speicherzugriffsfehler


I don't like including stuff like mpglib for file info when I rely on external decoders anyway... and I'm coding this with ThOrMA in mind, which means that I have all this nice metadata aready stored at some place. 
Nice would be a simple text file:

#audio track metadata begin
<samples>
<samplerate>
<channels>
<name>
<artist>
<album>
<...>
#audio track metadata end

no: too much. This smells like the xml export format that may be universally useful...

I'll stick to special .audiodata_ - files:

First line is the type: MP3, OGG, ...
then follow the number of samples and the samplerate (consider: mpg123 always gives 44100Hz, thus I don't need the rate for resampling but for calculation of the "real" samples and jump frames) and channel number.
then it's time for format-specific data, for MP3:

<samples-per-frame>


Check fullsample3 --- it makes segfault when converting 1152 samples to 1152 samples!
nono: its just getting a NULL pointer from the mixer! Apparently the device buffer pointer has to be volatile...
..not enough... or maybe I just got it wrong with marking the pointer as such volatile but only its data. 
Really nasty stuff, this volatile thingy. Got to sort this all out. Will be good for many headache attacks if I don't get it now.

Bad workaround for work until I got more docs: Allocating a big hump of memory in main thread on device creation.
Still have segfault on occasion while deleting device buffer.

Higher speed works btw... lower than 1 stops playback... after that I have hard clicking together with restarted playback. Nice.

GOTCHA! I worked on a bloody COPY of the device buffer. Of course tihis makes the original a permanent null pointer! Now made a audio_buffer class that makes thing a lot more clear.

But speed doesn't work anymore...
...does yet again... Tidied up a bit. More and more object files. Really not sure how stable I have this now. Had a sudden kill after starting to play the test track the second turn in parallel. Had a hang before while playing with two playbacks and speed.
But in principle it works... scary.

Undecided between really looking close at the safety (workability) of the code or first getting the mpg123 input right (with the right anmount of threads and the information bits.

HAD A CONFIRMED CRASH ON "play 0 test.mp3" on 2004-08-09! I should make a stress test per script for that.

Additionally: When play makes no sound (because mpg123_input isn't finished yet); a subsequent start hangs, socketeer waits but the mixer doesn't act. Looks like mpg123_input.play() not signalling.

BUG TO CONSIDER (tested once): set already end-reached title to follow gives a segmentation fault on the actual following.


We have come a long way already... BUT DAMMED WHY DOES THE BACKWATCH NOT GET RESPONSES FROM COMMANDS ANYMORE?!!!! Additionally: non-blocking io makes sense when near_end in readily... because it blocks occasionally on title end. But is near_end secure enough?
When the readily is already stuck in reading while "@P 0" comes... the mean way would be cancellation and recreation. Some signal might so it, too...

seeking doesn't work like this: PAUSE, wait for response, clear, JUMP, UNPAUSE
theory: mpg123 needs action to react to pause; meaning that I'll have to start clearing before any pause confirmation arrives.


END PHASE

I got now a kind of mixplayd replacement. pre-alpha. Testing now.
It goes quite well, but sometimes I have hangs or misbehaviour of mixplayer.pl:

Errors:
- hang at Track end (The old dilemma with blocking or non-blocking read; the first reliable in giving the decoder exactly thetime it needs to deliver audio, the latter safe in reckognizing a possible ending of the title)
- does the volume get turned up on cross-fading? Sound a bit strange sometimes...
	Yes, there is sth. maybe bass turned up? Check mixplayer...


The hangung at track end happens ... will always when I do blocking read.


-and there is this: mixplayer.pl hangs on load while:

have: 1344 example: 0
[main] playing device 0, called Helena
[backwatch] got string from decoder:
@P 0
[main] ordering 1344 samples (speed 1)
 for playback
we have devicebuffer at 0x8078270 and sampling buffer at 0x8063a64
fullsample: 0 insamples to 0
fullsample done...
have: 0 example: 0
[main] track ended
[main] playing device 0, called Helena


or so... (during crossfading):

fullsample: 1344 insamples to 1344
fullsample done...
have: 1344 example: 0
[main] playing device 0, called Helena
[main] ordering 1344 samples (speed 1)
 for playback
[main] ordering 1344 samples (speed 1)
 for playback
[backwatch] got string from decoder:
@P 0




So, what's mixer() waiting for? Why do I want to play 0 samples? Do I?
Strange issue...

The basic problem: I'm finally back there: How do I read the data from mpg123? All the select and non-blocking read stuff sucks. It should take exactly the time it should take. And I want it to work regardless of the prior knowledge of the exact title length (this knowledge can of course be used for less hassle, but there must be some safety layer).
When mpg123 says "@P 0" we know that it has finished decoding... or does it start the last frame? I assume. Makes sense. 
Mostly this signal comes just before the final read attempt, but sometimes not.

Backwatch sees @P 0 and should send some kind of immediate signal...

readily is:
1. waiting for its semaphore: the simple near_end does its job.
2. somewhere else in the code (calculations, sending messages to mpg123, ...)
3. already reading stuff from decoder

Don't know what I should think of point 2, but point 3 is quite likely.

How to handle that? The only signal I know at the moment is cancel. That will cause the read to return (do I get the number of bytes read up to then?); during dying readily could read all what is left (mixer should emtpy this buffer in several turns if needed). But I'll have to create a new readily afterwards. I could send a regular signal- but I'm not sure about the specialities with POSIX or linuxthreads there... Suppose I have some kind of signal. It should trigger two things: Stop readily reading stuff and see to the clearing of the pipe. When  readily is stuck, this signal handler would have to kill it then... think... think...


2004-08-14_0237

!!!!!!!!!!!!!!!!!! Got this. There's now a loop with care and eloquence. In principle this is solved.


Solved the bass up-turning: It was mixplayer not resetting the eq before loading new track.

Still there: two confirmed (partial) hangs: 

-during waiting of mixplayer, just after issuing a jump: silence
-on dload; old title still plays, socketeer hangs obviously

another issue: a track ending too early (carnival)

...and another one with no playback where socketeer is still active.

...and further: stuck in [read_audio] loop and a broken pipe for pitch command (how? dermixd was still running!)


2004-08-14

got one again!

A track ends, the dload blocks and dermixd:

[main] playing device 0, called Helena
[backwatch] got string from decoder:
@P 0
[backwatch]: locking... and unlocked... 
[main] ordering 1152 samples (speed 1)
 for playback
[mpg123_input] fetching 1152 samples
[read_audio]: going to read 1152 samples (4608 bytes)
[read_audio]: loop
[read_audio]: read 2048 bytes
[read_audio]: loop
[read_audio] loop done, 2560 bytes too short
[read_audio] loop ended because: end
we have devicebuffer at 0x806ed28 and sampling buffer at 0x806219c
fullsample done...
have: 512 example: 0
[main] playing device 0, called Helena

Strangestragnestrange. Next should have been 
[main] ordering samples
lets's insert a line for confirming that not the output is to blame...

again:
[read_audio]: loop
[read_audio]: loop
[read_audio]: loop
[read_audio]: loop
[read_audio]: loop
[read_audio]: loop

mixplayer:

playing**************************************
x - dee @ "virtual_frankenstein_goes_to_church" out of "virtual_frankenstein_goes_to_church"
*********************************************
end:590
Start fading
XFade: 0 (125 -> 0) ==> 1 (0 -> 82)
XFade: over: 70 (7s), in: 50 (5s), out: 30 (3s)
Done fading.
NextTrack: Got RVA values 3.4197 (3.4197) and 2.984227 (2.984227)
NormVol: Got 3.4197 and returning 148
FadeSet: Got -,-,-,-,-
FadeSet: Made: 7,3,5,0,0
FadeSet_Steps: over: 70, in: 50, out: 30
NextTrack: returning /mp3/mp3s/anne_clark/_the_best_of_anne_clark/22-killing_time.mp3
Somehow the command eq 0 1 1 1 did not get a response... I'm scared. (Datei oder Verzeichnis nicht gefunden)

dammed! why and how? Mixplayer wanted to set eq and pitch, followed by a load. On this channel there have been titles before... But it looks more like the end of the first track... wait: We have two issues:

-stuck in the read loop
-set_eq giving no response

So does the read loop have the power to block all else? Does sched_yield() actually work?


And yes - another hang on dload:

[read_audio]: going to read 1152 samples (4608 bytes)
[read_audio]: loop (near end: yes)
[read_audio]: read 2048 bytes
[read_audio]: loop (near end: yes)
[read_audio] loop done, 2560 bytes too short
[read_audio] loop ended because: end
we have devicebuffer at 0x74480808f0 and sampling buffer at 0x806419c
fullsample done...
have: 512 example: -296
[main] playing device 0, called Helena
[main] output done

So, it's not the output...

Another delight: 

/mp3/progs/mixplayd-control.pl getstat
88:88:88 ch 0 7 00:00..00:10 (0) [1.00] ch 1 stopped 01:55..00:10 (511) [1.00]

It's only the load action hanging!

Our issues narrow: Stuck in read loop on end and socketeers without answers.
(Don't forget the too early endings I think I saw...)

again dload stuck... looks like its somthing with the loader (cleaning of socket)

again loop hang: 

[read_audio]: loop (near end: no)
(repeat...)

so, there is nothing to read and backwatch doesn't set near_end


Got a likely cause. Seen the following line:

msglen of 10191 is greater than 10000

(and the like)

>>>>>> I forgot to re-initialize msglen!!!!!! Copied code, built another loop around it - bang!
>>>>>> I coded a limit for the amount of all communication done by a backwatch through all of its life!

And here is the point where I should take some hours reading the code, thinking, and writing properly. Checking for errors. Eventualities.


BugBugBug!!!!

I'll have to have a look at read_audio_nonblock:

[read_audio_nonblock]: clearing
[read_audio_nonblock]: done clearing

What happens next? Both seek and load seem to get stuck there sometimes.

Current work: in moving stuff into mixer_functions and the input class --- there I am currectly editing the beginning of load()


not implementing info through mpg123... the situation there is a bit unclear. For now assume the existence of an .audiodata file; if not: I'm just knowing the file type (and that mpg123 puts 44100Hz 16bit Stereo on the line).


Have I fixed the clearing error? I'm totally using the new read loop now. Hope it is safe. Doing some all-night-testing.

Still missing and important: when track ended with mpg123... a start or seek to 0 should be possible (be done automatically?) by loading the file again.

And: Parse some command line arguments. initial setup, daemonize or not... debug?

And: At some time in future, add alsa, arts, etc. output and other inputs sources (ogg, plain wav, cd-da(???)) and the named pipe feature (or another possibility to get a mix to disk).


This mpg123 is unbelievable: I'm trying to clear the pipe to eliminate all lost samples that may disturb the next playback; trying really hard - and then: mpg123 swallows these samples! Somehow it says "P 0" and decodes the last sample (its non-silent output claims that it has been played), but ITS SOUND COMES OUT WHEN THE NEXT TRACKS PLAYBACK STARTS!!!!!

load bla#
...
pause
load bla

This gives apparently the last frame (or n samples) of the first run just before the second.

Blast! 

(read part of the story in NOTES.skipclick)

Dirty solution: just ignoring 2304 samples after any seek. One may add some smart checking (p.ex. detect a constant silence and interpret it as the silence just before the start).
Considering smart checking: se TODO
   

