#include "GlobHdr.h"
#include "MetaCodecs.h"
#include "Formats.h"
#include "Header.h"
#include "Mem.h"
#include "TimLib:Seq.h"
#include "FStream.h"
#include "Channels.h"
#include "Desc.h"
#include "swis.h"
#include <stdio.h>
#include <string.h>
#include "Utils.h"
#include "IReader.h"
#include "ICodec.h"
#include "IWriter.h"
#include "Codecs.h"
#include "Log.h"

static const _kernel_oserror Err_StillHandler =
{ErrNum_PlayerError, "At least one DiskSample stream is still open."};
static const _kernel_oserror unknown_configure_code =
{ErrNum_ParamsError, "Unknown configuration code."};
static const _kernel_oserror Err_Bad_Stream_Handle =
{ErrNum_ParamsError, "Incorrect stream handle."};
static const _kernel_oserror Err_Source_Defined =
{ErrNum_ParamsError, "Stream is already assigned a source"};
static const _kernel_oserror Err_Bad_Source_Type =
{ErrNum_ParamsError, "Incorrect source type."};
static const _kernel_oserror Err_Bad_Output_Type =
{ErrNum_ParamsError, "Incorrect output type."};
static const _kernel_oserror Err_StreamIsNotReady =
{ErrNum_PlayerError, "The stream is not ready yet."};
static const _kernel_oserror Err_TooManyStreamHandlers =
{ErrNum_PlayerError, "Too many registered stream handlers."};
static const _kernel_oserror Err_UnknownParamCode =
{ErrNum_ParamsError, "Unknown parameter code."};
static const _kernel_oserror Err_Bad_Stream_Buffers =
{ErrNum_InternalError, "The stream buffers have been corrupted."};
static const _kernel_oserror Err_InvalidChannelNr =
{ErrNum_ParamsError, "Invalid channel number."};
static const char UnsupportedFileType[] = "Unsupported file type (%3x) or sub-type.";
static const char UnsupportedFileType2[] = "Unsupported file type (%3x, %s) or sub-type.";
static const char UnsupportedDescriptor[] = "Unsupported stream descriptor configuration.";

const _kernel_oserror* Streams_Init(GlobHdr* g, void* pw)
{
	memset(&g->streamsptr, 0, sizeof(g->streamsptr[0])*max_streams);

#ifdef MAKEABS
    pw = pw;
    return NULL;
#else
	return _swix(OS_AddCallBack, _INR(0,1), streams_callback_veneer, pw);
#endif
}

void Streams_CloseAll(GlobHdr* g)
{
	int i;

	for (i = 0; i < max_streams; i++)
	{
		if (g->streamsptr[i])
		{
			_kernel_swi_regs r;

			r.r[0] = i;
			swi_StreamClose(g, &r);
		}
	}
}

const _kernel_oserror* Streams_Finalise(GlobHdr* g, void* pw)
{
	int i;

	for (i = 0; i < max_streams; i++)
	{
		if (g->streamsptr[i])
			return &Err_StillHandler;
	}

#ifdef MAKEABS
    pw = pw;
#else
	// Remove possible pending CallAfter
	_swix(OS_RemoveTickerEvent, _INR(0,1), streams_callevery_veneer, pw);

	// Remove possible pending Callback
	if (g->flags & glb_flag_callback)
	{
		_swix(OS_RemoveCallBack, _INR(0,1), streams_callback_veneer, pw);
		g->flags &= ~glb_flag_callback;
	}
#endif

	return NULL;
}

#ifndef MAKEABS
_kernel_oserror* Streams_CalledEvery(_kernel_swi_regs* r, void* pw)
{
	GlobHdr* g = Glb;

	IGNORE(r);

	g->flags |= glb_flag_callback;
	_swix(OS_AddCallBack, _INR(0,1), streams_callback_veneer, pw);

	return NULL;
}

_kernel_oserror* Streams_Callback(_kernel_swi_regs* r, void* pw)
{
	GlobHdr* g = Glb;
	int i;
	int32_t timer[8];
	int64_t time, timec;

	CSeq_GetTime(g, &timer[4]);

	IGNORE(r);

	for (i = 0; i < max_streams; i++)
	{
		IStream* s = g->streamsptr[i];
		if (s)
		{
			IStream_BackgroundProcess(g, s);
		}
	}

	CSeq_GetTime(g, &timer[0]);
	timer[0] -= timer[4]; // diff between cs info
	timer[5] -= timer[1]; // diff between decreasing counter info
	if ((timer[0] == 0) && (timer[5] < 0))
		 timer[5] += timer[3]; // counter diff < 0, add counter period
	time = timer[0];        // cs diff in [sec / 100]
	timec = timer[5];       // counter diff in [sec / counter freq]
	timec *= 100;           // counter diff in [sec / 100 * counter freq]
	time *= timer[2];       // cs diff in [sec / 100 * counter freq]
	time += timec;          // total diff in [sec / 100 * counter freq]
	time *= 500;            // *1000 and divide by 2 cs (cf call every) = (2 * counter freq)
	time /= timer[2];       // CPU in [1 / 1000]
    g->cpu[g->cpu_slot] = (int) time;
    g->cpu_slot++;
    if (g->cpu_slot >= CPU_SLOTS)
    	g->cpu_slot = 0;

	g->flags &= ~glb_flag_callback;

	_swix(OS_CallAfter, _INR(0,2), 2, streams_callevery_veneer, pw);

	return NULL;
}
#endif

static const _kernel_oserror* swi_Configure_InputBufferSize(GlobHdr* g, _kernel_swi_regs* r)
{
	if ((r->r[1] >= 256)
	&&  (r->r[1] <= 2048))
		g->cfg.inputbufsize = r->r[1];

	r->r[1] = g->cfg.inputbufsize;

	return NULL;
}

static const _kernel_oserror* swi_Configure_OutputBufferSize(GlobHdr* g, _kernel_swi_regs* r)
{
	if ((r->r[1] >= 64)
	&&  (r->r[1] <= 4096))
		g->cfg.outputbufsize = r->r[1];

	r->r[1] = g->cfg.outputbufsize;

	return NULL;
}

static const _kernel_oserror* swi_Configure_IcyMetadata(GlobHdr* g, _kernel_swi_regs* r)
{
	if (r->r[1] == 0)
		g->flags &= ~glb_flag_icymeta;
	if (r->r[1] == 1)
		g->flags |= glb_flag_icymeta;

	r->r[1] = (g->flags & glb_flag_icymeta) ? 1 : 0;

	return NULL;
}

static const _kernel_oserror* swi_Configure_AudiocastMetadata(GlobHdr* g, _kernel_swi_regs* r)
{
	if (r->r[1] == 0)
		g->flags &= ~glb_flag_udpmeta;
	if (r->r[1] == 1)
		g->flags |= glb_flag_udpmeta;
	if (r->r[2] > 0)
		g->cfg.audiocastport = r->r[2];

	r->r[1] = (g->flags & glb_flag_udpmeta) ? 1 : 0;
	r->r[2] = g->cfg.audiocastport;

	return NULL;
}

static const _kernel_oserror* swi_Configure_Buffering(GlobHdr* g, _kernel_swi_regs* r)
{
	if ((r->r[1] >= 1)
	&&  (r->r[1] <= 60))
		g->cfg.buffering = r->r[1];

	r->r[1] = g->cfg.buffering;

	return NULL;
}

const _kernel_oserror* swi_Configure_Streams(GlobHdr* g, _kernel_swi_regs* r)
{
	switch(r->r[0])
	{
		case 0x100: return swi_Configure_InputBufferSize(g, r);
		case 0x101: return swi_Configure_OutputBufferSize(g, r);
		case 0x102: return swi_Configure_IcyMetadata(g, r);
		case 0x103: return swi_Configure_AudiocastMetadata(g, r);
		case 0x104: return swi_Configure_Buffering(g, r);
	}

	return &unknown_configure_code;
}

static const _kernel_oserror* IStream_GetHeader(GlobHdr* g, unsigned int i, IStream** pps)
{
	if ((i >= max_streams)
	||  !g->streamsptr[i])
		return &Err_Bad_Stream_Handle;

	*pps = g->streamsptr[i];

	return NULL;
}

static const _kernel_oserror* IStream_GetReadyHeader(GlobHdr* g, unsigned int i, IStream** pps)
{
	if ((i >= max_streams)
	||  !g->streamsptr[i])
		return &Err_Bad_Stream_Handle;

	*pps = g->streamsptr[i];

	if ((*pps)->state == EIStream_State_FatalError)
		return &(*pps)->LastError;

	if ((*pps)->state < EIStream_State_Buffering)
		return &Err_StreamIsNotReady;

	return NULL;
}

static MetaCodec metas[] =
{ Meta_ID3v2
, Format_WAVE
, Meta_Riff
, NULL
};

static ICodec* codecs[] =
{ &AC3_Codec
, &PCM_Codec
, &Vorbis_Codec
, &FLAC_Codec
// , &APE_Codec
, &MP3_Codec
, NULL
};


/*------------------------------------------------------------------------------
 * Memory management
 */

const _kernel_oserror* IStream_Alloc(IStream* s, void** pp, size_t size)
{
	return CMem_Alloc(s->pGlobHdr, pp, size);
}

const _kernel_oserror* IStream_AllocString(IStream* s, char** pp, const char* pref)
{
	return CMem_AllocString(s->pGlobHdr, pp, pref);
}

void IStream_Free(IStream* s, void** pp)
{
	CMem_Free(s->pGlobHdr, *pp);
	*pp = NULL;
}

/*------------------------------------------------------------------------------
 * Metadata info management
 */

void IStream_ClearMetadata(IStream* s, unsigned int level)
{
	GlobHdr* g = s->pGlobHdr;
	Meta* meta = s->meta;

	while (meta)
	{
		if (meta->level >= level)
		{
			// clear contents
			CMem_Free(g, meta->data);
			IStream_AllocString(s, (char**) &meta->data, NULL);
			meta->size = 0;

			_swix(OS_ReadMonotonicTime, _OUT(0), &meta->timestamp);
			// do some broadcasting here
			s->info.TimeStamp = meta->timestamp;
		}

		meta = meta->pnext;
	}
}

Meta* IStream_FindMetadata(IStream* s, unsigned int id)
{
	Meta* meta = s->meta;

	// try to find an occurence
	while (meta)
	{
		if (meta->id == id)
			break;

		meta = meta->pnext;
	}

	return meta;
}

const _kernel_oserror* IStream_SetMetadata(IStream* s
                                   , unsigned int level
                                   , unsigned int id
                                   , const void* data
                                   , unsigned int size)
{
	Meta* meta = IStream_FindMetadata(s, id);
	const _kernel_oserror* e = NULL;

	// if not found reserve an occurence
	if (!meta)
	{
		e = IStream_Alloc(s, (void**) &meta, sizeof(*meta));
		if (e) return e;
		meta->pnext = s->meta;
		meta->timestamp = 0;
		meta->id = id;
		e = IStream_AllocString(s, (char**) &meta->data, NULL);
		if (e)
		{
			IStream_Free(s, (void**) &meta);
			return e;
		}

		s->meta = meta;
	}
	meta->level = level;

	// replace old occurence
	if ((meta->size != size)
	||  memcmp(meta->data, data, size))
	{
		void* newdata = NULL;
		e = IStream_Alloc(s, (void**) &newdata, size);
		if (e) return e;
		memcpy(newdata, data, size);
		IStream_Free(s, &meta->data);
		_swix(OS_ReadMonotonicTime, _OUT(0), &meta->timestamp);
		meta->data = newdata;
		meta->size = size;
		// do some broadcasting here
		s->info.TimeStamp = meta->timestamp;
	}

	return NULL;
}

const _kernel_oserror* IStream_SetText(IStream* s
                               , unsigned int level
                               , unsigned int id
                               , const char* data
                               , unsigned int size)
{
	Meta* meta = IStream_FindMetadata(s, id);
	const _kernel_oserror* e = NULL;

	// if not found reserve an occurence
	if (!meta)
	{
		e = IStream_Alloc(s, (void**) &meta, sizeof(*meta));
		if (e) return e;
		meta->pnext = s->meta;
		meta->timestamp = 0;
		meta->id = id;
		e = IStream_AllocString(s, (char**) &meta->data, NULL);
		if (e)
		{
			IStream_Free(s, (void**) &meta);
			return e;
		}

		s->meta = meta;
	}
	meta->level = level;

	char* newdata = NULL;

	if (size == 0)
	{
		e = IStream_AllocString(s, &newdata, data);
		if (e) return e;
	}
	else
	{
		e = IStream_Alloc(s, (void**) &newdata, size + 1);
		if (e) return e;
		memcpy(newdata, data, size);
		newdata[size] = '\0';
	}
	String_StripBlanks(newdata);
	Log("Meta %d: %s", id, newdata);

	// replace old occurence
	if (strcmp(meta->data, newdata))
	{
		IStream_Free(s, &meta->data);
		_swix(OS_ReadMonotonicTime, _OUT(0), &meta->timestamp);
		meta->data = newdata;
		meta->size = strlen(newdata) + 1;
		// do some broadcasting here
		s->info.TimeStamp = meta->timestamp;
	}
	else IStream_Free(s, (void*) &newdata);

	return NULL;
}

/*------------------------------------------------------------------------------
 * Input buffer related stuff
 */

// Must be used after any buffer filling

void IStream_SaveInb(IStream* s)
{
	int ioff = _kernel_irqs_disabled();
	int used;

	if (!ioff) _kernel_irqs_off();
	s->saved_inb.free = s->inb->free;
	s->saved_inb.start = s->inb->start;

	// replace saved start by number of bytes in buffer
	used = s->saved_inb.free - s->saved_inb.start;
	if (used < 0) used += s->saved_inb.size;
	s->saved_inb.start = used;
	if (!ioff) _kernel_irqs_on();
}

static int IStream_CheckIntegrity(IStream* s)
{
	int ioff = _kernel_irqs_disabled();
	int nok = 0;
	int used;
	inb_t copy;

	if (!ioff) _kernel_irqs_off();
	copy = *s->inb;
	copy.finishflag = 0; // ignore that flag
	if (!ioff) _kernel_irqs_on();

	// get number of bytes in buffer
	used = copy.free - copy.start;
	if (used < 0) used += copy.size;

	// that value can only move toward zero
	if ((used < 0)
	||  (used > s->saved_inb.start)
	||  (copy.free < 0)
	||  (copy.free >= copy.size))
		nok = 1;
	else
	{
		// store updated value and check that the rest
		// remained unchanged
		s->saved_inb.start = copy.start = used;
		nok = memcmp(&s->saved_inb, &copy, sizeof(s->saved_inb));
	}

	if (nok)
	{
		Log("Saved vs current");
		LogBinary(&s->saved_inb, sizeof(s->saved_inb));
		LogBinary(&copy, sizeof(copy));
		s->LastError = Err_Bad_Stream_Buffers;
		s->state = EIStream_State_FatalError;
	}

	return nok;
}

// Returns -1 if not enough bytes in buffer
// Returns -2 if line too long

int IStream_ReadTextLine(IStream* s, char* p, int size)
{
	uint8_t* ppos = s->bitb.start;
	int read = size;

	while ((ppos != s->bitb.free) && size > 0)
	{
		*p++ = (char) *ppos++;
		if (ppos > s->bitb.last)
			ppos = s->bitb.data;
		if (p[-1] < ' ')
			goto Done;
		size--;
	}

	// failed to read line
	if (size <= 0)
		return -2; // line too long

	return -1;

Done:
	read -= size;
	bs_skipBytes(&s->bitb, read);
	p--;
	if (((p[0] == '\r') && (*ppos == '\n'))
	||  ((p[0] == '\n') && (*ppos == '\r')))
		bs_skipBytes(&s->bitb, 1);
	p[0] = '\0';

	return read;
}

// Returns position of input ptr (e.g. file pos - nr of bytes in input buffer)
int IStream_GetInputPos(const IStream* s)
{
	return s->source.pos - ((bs_bitcount(&s->bitb) + 7) >> 3);
}

// Returns 1 if input method seekable, 0 otherwise
int IStream_IsSeekable(IStream* s)
{
	return (s->reader.fn->Seek != NULL);
}

// Set byte offset in stream
const _kernel_oserror* IStream_Seek(IStream* s, int position)
{
	if (s->reader.fn->Seek)
		return s->reader.fn->Seek(s, position);

	return NULL;
}

unsigned int IStream_SkipBytes(IStream* s, unsigned int N)
{
	N = bs_skipBytes(&s->bitb, N);

	// Don't do anything more while IO are delayed.
	if ((s->status & swrk_status_RD_nodelay)
	&&  (N > 0)
	&&  (s->reader.fn->Seek))
	{
		s->reader.fn->Seek(s, s->source.pos + N);
		N = 0;
	}

	return N;
}

// Returns 1 if input method allows to user to set define new pos, 0 otherwise
int IStream_IsPositionable(IStream* s)
{
	return (s->reader.fn->SetPos != NULL);
}

// Set position (either byte offset in stream or for externally filled FromDesc a virtual one)
const _kernel_oserror* IStream_SetPos(IStream* s, int position)
{
	if (s->reader.fn->SetPos)
	{
		s->reader.chunksize = (s->inb->size >> 1);

		return s->reader.fn->SetPos(s, position);
	}

	return NULL;
}

static const _kernel_oserror* IStream_ProcessMeta(IStream* s, int* bCont)
{
	const _kernel_oserror* e;
	bool bContinue;
	int pos, size, free;
	int ioff = _kernel_irqs_disabled();

	// Copy buffers info so that we are not bothered by interrupts
	if (!ioff) _kernel_irqs_off();
	s->bitb.start = s->bitb.data + s->inb->start;
	if (s->bitb.start < s->bitb.data) s->bitb.start = s->bitb.data;
	if (s->bitb.start > s->bitb.last) s->bitb.start = s->bitb.last;
	s->bitb.free  = s->bitb.data + s->inb->free;
	if (s->bitb.free < s->bitb.data) s->bitb.free = s->bitb.data;
	if (s->bitb.free > s->bitb.last) s->bitb.free = s->bitb.last;
	if (!ioff) _kernel_irqs_on();

	s->bitb.count = s->bitb.free - s->bitb.start;
	if (s->bitb.count < 0) s->bitb.count += s->bitb.size;
	s->bitb.count <<= 3;
	s->bitb.bitindex = 0;

	// Assume there is more to process
	*bCont = 1;

	if (s->fnmeta)
	{
		e = s->fnmeta(s, &bContinue);

		// Update buffers
		if (!ioff) _kernel_irqs_off();
		s->inb->start = s->bitb.start - s->bitb.data;
		if (!ioff) _kernel_irqs_on();

		if (e || !bContinue)
			s->fnmeta = NULL;

		if (e) return e;

		// still requires extra processed?
		if (bContinue) return NULL;
	}

	// Till no buffer is consumed
	// or a codec is proposed
	do
	{
		MetaCodec* meta = &metas[0];

		pos = s->inb->start;

		// Loop on codecs till a footer was found
		while ((*meta != NULL) && !s->codec.prefn)
		{
			e = (*meta)(s, &bContinue);

			// Update buffers
			if (!ioff) _kernel_irqs_off();
			s->inb->start = s->bitb.start - s->bitb.data;
			if (!ioff) _kernel_irqs_on();

			if (e) return e;

			// still requires extra processed?
			if (bContinue)
			{
				s->fnmeta = *meta;
				return NULL;
			}

			meta++;
		}

	} while ((pos != s->inb->start) && !s->codec.prefn);

	// No, they were all processed
	*bCont = 0;

	if (IStream_IsSeekable(s))
	{
		// Ensure that we have not already loaded more data
		// than the data chunk (WAV) contains or the rest will be
		// used as data
		if (!ioff) _kernel_irqs_off();
		size = s->inb->free - s->inb->start;
		if (size < 0) size += s->inb->size;

		if (size > (s->source.end - s->source.start))
		{
			free = s->inb->free - size
			     + (s->source.end - s->source.start);
			if (free < 0) free += s->inb->size;
			s->inb->free = free;
		}
		if (!ioff) _kernel_irqs_on();
	}

	return NULL;
}

const _kernel_oserror* IStream_Accept(IStream* s, const Desc* desc)
{
	const _kernel_oserror* e;
	ICodec** pcodec = codecs;

	while (*pcodec)
	{
		e = (*pcodec)->Accept(s, desc);
		if (e) return e;

		if (s->codec.prefn) return NULL;

		pcodec++;
	}

	if (0x8000 >= (int) desc->filetype)
		Log("Tried to use unknown codec %s", desc->filetype);

	return ErrorFromString(ErrNum_UnsupportedFormat, UnsupportedDescriptor);
}

static const _kernel_oserror* IStream_GetCodec(IStream* s)
{
	const _kernel_oserror* e;
	ICodec** pcodec = codecs;

	// Close any previously used codec
	if (s->codec.fn)
	{
		s->codec.fn->Unload(s);
		s->codec.fn = NULL;
	}

	while (*pcodec)
	{
		e = (*pcodec)->Load(s);
		if (e)
		{
			(*pcodec)->Unload(s);
			return e;
		}

		if (s->codec.fn) return NULL;

		pcodec++;
	}

	if (s->source.mimetype)
		return ErrorFromString(ErrNum_UnsupportedFormat, UnsupportedFileType2, s->source.os_filetype, s->source.mimetype);

	return ErrorFromString(ErrNum_UnsupportedFormat, UnsupportedFileType, s->source.os_filetype);
}

const const _kernel_oserror* IStream_NewOutputBuffer(IStream* s, int32_t* psize, int32_t* parea, void** pdata)
{
	const _kernel_oserror* e;
#ifndef MAKEABS
	char title[32];
#endif

	if (*psize == 0)
		*psize = s->pGlobHdr->cfg.outputbufsize << 10;

#ifdef MAKEABS
	e = CMem_Alloc(s->pGlobHdr, pdata, *psize);
	if (e)
		*parea = 0;
	else
		*parea = (int32_t) *pdata;
#else
	snprintf(title, sizeof(title), "DiskSample (Output %d)", s->index);
	e = _swix(OS_DynamicArea, _INR(0,8)|_OUT(1)|_OUT(3)
		, 0, -1, *psize, -1, 0x80
		, *psize, 0, 0, title
		, parea, pdata);
#endif

	return e;
}

void IStream_DeleteOutputBuffer(IStream* s, int32_t* parea)
{
	if (*parea)
	{
#ifdef MAKEABS
		CMem_Free(s->pGlobHdr, (void*) *parea);
#else
		IGNORE(s);

		_swix(OS_DynamicArea, _INR(0,1), 1, *parea);
#endif

		*parea = 0;
	}
}

/*------------------------------------------------------------------------------
 * Stream creation/destruction
 */

static const _kernel_oserror* IStream_New(GlobHdr* g, int* index, int size)
{
	const _kernel_oserror* e;
	int i;
	IStream* s;
	char title[64];

	for (i = 0; i < max_streams; i++)
	{
		if (!g->streamsptr[i])
			break;
	}

	if (i == max_streams)
		return &Err_TooManyStreamHandlers;

	e = CMem_Alloc(g, (void**) &s, sizeof(*s));
	if (e) return e;

	g->streamsptr[i] = s;
	*index = i;

	memset(s, 0, sizeof(*s));
	s->index = i;
	s->Tag[0] = 'S';
	s->Tag[1] = 'T';
	s->Tag[2] = 'R';
	s->Tag[3] = 'M';
	s->pGlobHdr = g;
	s->state = EIStream_State_NoSource;
	s->writer.volume = 256;
	s->chained_handle = -1;

#ifdef MAKEABS
	// Keep dynamic area, cf background work in external modules
#endif
	snprintf(title, sizeof(title), "DiskSample (Input %d)", i);
	e = _swix(OS_DynamicArea, _INR(0,8)|_OUT(1)|_OUT(3)
		, 0, -1, size + 16*4, -1, 0x80
		, size + 16*4, 0, 0, title
		, &s->inb_area, &s->inb);
	if (e) goto Error;

	memset(s->inb, 0, sizeof(*s->inb));
	s->saved_inb.size = s->inb->size = s->bitb.size = size;
	s->bitb.data = (byte*) (s->inb + 1);
	s->bitb.last = s->bitb.data + s->bitb.size - 1;

	// Save buffers state
	IStream_SaveInb(s);

	return NULL;

Error:
	if (s->inb_area) _swix(OS_DynamicArea, _INR(0,1), 1, s->inb_area);
	CMem_Free(g, s);
	g->streamsptr[i] = NULL;

	return e;
}

static const _kernel_oserror* IStream_InputOpen(IStream* s, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;

	IStream_Free(s, (void**) &s->source.mimetype);
	s->source.os_filetype = -1;
	s->codec.prefn = NULL;

#ifndef MAKEABS
	if (!s->writer.fn)
	{
		s->writer.fn = &ToSpeakers;
		e = s->writer.fn->Initialize(s, NULL);
	}
#endif

	s->reader.chunksize = (s->inb->size >> 1);

	e = s->reader.fn->Open(s, r);
	if (e) return e;

	return e;
}

static void IStream_InputClose(IStream* s)
{
	if (s->reader.fn)
	{
		s->state = EIStream_State_NoSource;

		s->reader.fn->Close(s);
		s->reader.fn = NULL;

		IStream_Free(s, (void**) &s->source.mimetype);
		s->source.os_filetype = -1;
	}
}

static void IStream_Destroy(IStream* s)
{
	GlobHdr* g = s->pGlobHdr;
	Meta* meta = s->meta;

	// Clear type specific stuff
	if (s->codec.fn) s->codec.fn->Unload(s);

	if (s->writer.fn && s->writer.data)
	{
		s->writer.fn->Finalize(s);
		s->writer.fn = NULL;
	}

	// Close Input
	IStream_InputClose(s);

	// clear common stuff
	if (s->inb_area) _swix(OS_DynamicArea, _INR(0,1), 1, s->inb_area);

	while (meta)
	{
		Meta* pnext = meta->pnext;

		CMem_Free(g, meta->data);
		CMem_Free(g, meta);

		meta = pnext;
	}

	CMem_Free(g, s);
	CMem_Shrink(g);
}

/*------------------------------------------------------------------------------
 * Stream SWIs
 */

/*------------------
 * SWI StreamCreate
 *
 * In  - R0  flags = 0
 *       R1  input buffer size in KB
 *
 * Out - R0  handle
 */

const _kernel_oserror* swi_StreamCreate(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	int index, size;
	IStream* s;

	size = r->r[1];

	if (size < 4) size = 4;
	if (size > 1024) size = 1024;

	e = IStream_New(g, &index, size << 10);
	if (e) return e;

	s = g->streamsptr[index];

	r->r[0] = index;
	r->r[1] = (int) s->inb;

	return NULL;
}

/*--------------
 * SWI FileOpen
 *
 * In  - R1  filename ptr
 *
 * Out - R0  handle
 */

const _kernel_oserror* swi_FileOpen(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	int index;
	IStream* s;

	e = IStream_New(g, &index, g->cfg.inputbufsize << 10);
	if (e) return e;

	s = g->streamsptr[index];

	s->reader.fn = &FromFile;
	e = IStream_InputOpen(s, (_kernel_swi_regs*) &r->r[1]);
	if (e) goto Error;

	// Ensure buffer will start to be filled
	while (    (s->state != EIStream_State_FatalError)
		&& (s->state < EIStream_State_CodecInit))
		IStream_BackgroundProcess(g, s);

	if (s->state == EIStream_State_FatalError)
	{
		_kernel_oserror* er;
		// Some memory leak here
		e = CMem_Alloc(g, (void**) &er, sizeof(*er));
		if (!e)
		{
			e = er;
			*er = s->LastError;
		}
	}

Error:
	if (e)
	{
		IStream_Destroy(s);
		g->streamsptr[index] = NULL;
	}
	else
		r->r[0] = index;

	return e;
}

/*------------------
 * SWI StreamSource
 *
 * In  - R0  handle
 *       R1  input type
 *       R2... parameters
 *
 * Out - for input type = 0
 *       R1  input buffer ptr
 */

const _kernel_oserror* swi_StreamSource(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IStream* s;

	e = IStream_GetHeader(g, r->r[0], &s);
	if (e) return e;

	if (s->state != EIStream_State_NoSource)
		return &Err_Source_Defined;

	switch(r->r[1])
	{
		case 0: // Desciption block
			s->status |= swrk_status_noloop;
			s->reader.fn = &FromDesc;
			r->r[1] = (int) s->inb;
		break;
		case 1: // File
			s->reader.fn = &FromFile;
		break;
		case 2: // Url
			s->status |= swrk_status_noloop;
			s->reader.fn = &FromHttp;
		break;
		case 3: // FileExt
			s->reader.fn = &FromFileExt;
		break;
		case 4: // CD
			s->reader.fn = &FromCD;
		break;
		default:
			return &Err_Bad_Source_Type;
	}

	e = IStream_InputOpen(s, (_kernel_swi_regs*) &r->r[2]);
	if (e)
	{
		s->LastError = *e;
		IStream_InputClose(s);
		e = &s->LastError;
	}

	return e;
}

/*--------------------
 * SWI StreamReceiver
 *
 * In  - R0  handle
 *       R1  output type
 *       R2... parameters
 */

const _kernel_oserror* swi_StreamReceiver(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IStream* s;

	e = IStream_GetHeader(g, r->r[0], &s);
	if (e) return e;

	if ((s->state != EIStream_State_NoSource)
	||  (s->writer.fn != NULL))
		return &Err_Source_Defined;

	switch(r->r[1])
	{
#ifndef MAKEABS
		case 0: // Normal output to speakers
			s->writer.fn = &ToSpeakers;
		break;
		case 1: // Write input to file and skip decoding phase
			s->writer.fn = &ToSrcFile;
		break;
#endif
		case 2: // Write output to file
			s->writer.fn = &ToWAVEFile;
		break;
		default:
			return &Err_Bad_Output_Type;
	}

	e = s->writer.fn->Initialize(s, (_kernel_swi_regs*) &r->r[2]);
	if (e)
	{
		s->LastError = *e;
		s->writer.fn->Finalize(s);
		s->writer.fn = NULL;
		e = &s->LastError;
	}
	else
	{
		s->writer.fn->Volume(s, &s->writer.volume);
	}

	return e;
}

/*-----------------
 * SWI StreamClose
 *
 * In  - R0  handle
 *
 * Out -
 */

const _kernel_oserror* swi_StreamClose(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	int index;
	IStream* s;

	// Stop playing
	e = swi_StreamPause(g, r);
	if (e) return e;

	index = r->r[0];
	s = g->streamsptr[index];

	// Remove handle
	g->streamsptr[index] = NULL;

	// Clear memory
	IStream_Destroy(s);

	return NULL;
}

/*----------------
 * SWI StreamPlay
 *
 * In  - R0  handle
 *
 * Out -
 */

const _kernel_oserror* swi_StreamPlay(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IStream* s;
	int ioff;

	e = IStream_GetReadyHeader(g, r->r[0], &s);
	if (e) return e;

	// Don't do anything if already playing
	if (s->status & swrk_status_playing)
		return NULL;

	ioff = _kernel_irqs_disabled();
	if (!ioff) _kernel_irqs_off();

	// Mark file as playing
	s->status |= swrk_status_playing;
	s->status &= ~swrk_status_paused;

	// Determine number of soft & hard channels
	e = s->writer.fn->Open(s);

	if (!ioff) _kernel_irqs_on();

	// Reset CPU
	g->cpu_max = 0;

	return e;
}

/*-----------------
 * SWI StreamPause
 *
 * In  - R0  handle
 *
 * Out -
 */

const _kernel_oserror* swi_StreamPause(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IStream* s;

	e = IStream_GetHeader(g, r->r[0], &s);
	if (e) return e;

	// Is file loaded and playing ?
	if (s->status & swrk_status_playing)
	{
		int ioff = _kernel_irqs_disabled();
		if (!ioff) _kernel_irqs_off();

		s->status &= ~swrk_status_playing;
		s->status |= swrk_status_paused;
		s->writer.fn->Close(s);

		if (!ioff) _kernel_irqs_on();
	}

	return NULL;
}

/*----------------
 * SWI StreamStop
 *
 * In  - R0  handle
 *
 * Out -
 */

const _kernel_oserror* swi_StreamStop(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	int save;

	// Stop playing
	e = swi_StreamPause(g, r);
	if (e) return e;

	save = r->r[1];
	r->r[1] = 0;
	e = swi_StreamPosition(g, r);
	r->r[1] = save;

	return e;
}

/*--------------------
 * SWI StreamPosition
 *
 * In  - R0  handle
 *       R1  new position, -1 to read, -2 to return also duration
 *
 * Out - R1  Time Index (in ms)
 *       R2  Duration (in ms) if R1 =-2 on entry
 */

const _kernel_oserror* swi_StreamPosition(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IStream* s;
	int64_t pos, duration;
	int r1 = r->r[1];

	// Preset
	if (r1 == -2)
		r->r[2] = 0;
	r->r[1] = 0;

	e = IStream_GetReadyHeader(g, r->r[0], &s);
	if (e) return e;

	// Playing without decoding?
	if (!s->codec.fn)
		return NULL;

	// Set new position?
	if ((r1 != -1)
	&&  (r1 != -2))
	{
		if (IStream_IsPositionable(s))
		{
			// Set type specific stuff
			e = s->codec.fn->SetPos(s, (int64_t) r1);
			if (e) return e;
			s->status &= ~(swrk_status_decoded | swrk_status_played);
		}
	}
	// Read current position
	e = s->codec.fn->ReadPos(s, &pos);
	if (e) return e;
	if (r1 == -2)
	{
		e = s->codec.fn->ReadDuration(s, &duration);
		if (e) return e;
		if (duration > 0x7fffffff)
			duration = 0x7fffffff;
		r->r[2] = (int) duration;
	}
	if (pos > 0x7fffffff)
		pos = 0x7fffffff;
	r->r[1] = (int) pos;

	return NULL;
}

/*------------------
 * SWI StreamVolume
 *
 * In  - R0  handle
 *       R1  new volume [0...256...1024], -1 to read
 *
 * Out - R1  current volume [0...256...1024]
 */

const _kernel_oserror* swi_StreamVolume(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IStream* s;
	int volume;

	e = IStream_GetHeader(g, r->r[0], &s);
	if (e) return e;

	if ((r->r[1] >= 0)
	&&  (r->r[1] <= 1024))
		volume = r->r[1];
	else
		volume = -1;

	if (s->writer.fn)
	{
		s->writer.fn->Volume(s, &volume);
		s->writer.volume = volume;
	}
	else
	{
		if (volume >= 0)
			s->writer.volume = volume;
		volume = s->writer.volume;
	}

	r->r[1] = volume;

	return NULL;
}

/*-------------------
 * SWI StreamIsReady
 *
 * In  - R0  handle
 *
 * Out - R1  1 true, 0 false
 */

const _kernel_oserror* swi_StreamIsReady(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IStream* s;

	r->r[1] = 0;

	e = IStream_GetHeader(g, r->r[0], &s);
	if (e) return e;

	if (s->state == EIStream_State_FatalError)
		return &s->LastError;

	if (s->state >= EIStream_State_CodecReady)
		r->r[1] = 1;

	return NULL;
}

/*------------------
 * SWI StreamStatus
 *
 * In  - R0  handle
 *       R1  status
 *       R2  mask
 *
 * Out - R1  current status
 *           bit  meaning when set
 *           0    song is ready
 *           1    song is playing
 *           2    song is paused
 *           3    don't loop when song ends
 */

const _kernel_oserror* swi_StreamStatus(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IStream* s;
	int value, mask;
	int ioff;

	e = IStream_GetHeader(g, r->r[0], &s);
	if (e) return e;

	if (s->state == EIStream_State_FatalError)
		return &s->LastError;

	// just check if stream returns an error
	if (s->codec.fn)
	{
		e = s->codec.fn->InError(s);
		if (e) return e;
	}

	ioff = _kernel_irqs_disabled();
	if (!ioff) _kernel_irqs_off();

	// can only alter some flags
	mask = r->r[2] & swrk_status_altermask;
	value = r->r[1] & mask;
	s->status &= ~mask;
	s->status |= value;

	if (!ioff) _kernel_irqs_on();

	// get status
	r->r[1] = s->status & swrk_status_readmask;

	return NULL;
}

/*----------------
 * SWI StreamChain
 *
 * In  - R0  handle
 *       R1  chained handle, -2 to read, -1 to clear chain
 *
 * Out - R1  current handle
 */

const _kernel_oserror* swi_StreamChain(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IStream* s;
	IStream* sc;

	e = IStream_GetHeader(g, r->r[0], &s);
	if (e) return e;

	if (r->r[1] != -2)
	{
		if (r->r[2] != -1)
		e = IStream_GetHeader(g, r->r[1], &sc);
		if (e) return e;

		s->chained_handle = r->r[1];
	}

	r->r[1] = s->chained_handle;

	return NULL;
}

/*----------------
 * SWI StreamInfo
 *
 * In  - R0  handle
 *
 * Out - R1  File Length (in bytes)
 *       R2  Time Length (in ms)
 *       R3  max nr of playing channels
 */

const _kernel_oserror* swi_StreamInfo(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IStream* s;
	int64_t duration;

	r->r[1] = 0;
	r->r[2] = 0;
	r->r[3] = 0;

	e = IStream_GetReadyHeader(g, r->r[0], &s);
	if (e) return e;

	r->r[1] = s->source.length;

	// Playing without decoding?
	if (!s->codec.fn)
		return NULL;

	e = s->codec.fn->ReadDuration(s, &duration);
	if (e) return e;
	if (duration > 0x7fffffff)
		duration = 0x7fffffff;

	r->r[2] = (int) duration;
	r->r[3] = s->info.Channels;

	return NULL;
}

/*-----------------
 * SWI StreamTexts
 *
 * In  - R0  handle
 *
 * Out - R1  title: pointer to a 0 terminated string
 *       R2  author: pointer to a 0 terminated string
 *       R3  album: pointer to a 0 terminated string
 *       R4  type: pointer to a 0 terminated string
 */

static const char nil = 0;

const _kernel_oserror* swi_StreamTexts(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IStream* s;
	Meta* meta;

	// Avoid null pointers for callers not checking errors
	r->r[1] = r->r[2] = r->r[3] = r->r[4] = (int) &nil;

	e = IStream_GetReadyHeader(g, r->r[0], &s);
	if (e) return e;

	meta = IStream_FindMetadata(s, EMeta_StreamTitle);
	r->r[1] = (int)((meta && meta->data) ? meta->data : &nil);
	meta = IStream_FindMetadata(s, EMeta_StreamAuthor);
	r->r[2] = (int)((meta && meta->data) ? meta->data : &nil);
	meta = IStream_FindMetadata(s, EMeta_StreamAlbum);
	r->r[3] = (int)((meta && meta->data) ? meta->data : &nil);
	meta = IStream_FindMetadata(s, EMeta_StreamType);
	r->r[4] = (int)((meta && meta->data) ? meta->data : &nil);

	return NULL;
}

/*-----------------
 * SWI StreamParam
 *
 * In  - R0  handle
 *       R1  code
 *
 * Out - R2...
 */

const _kernel_oserror* swi_StreamParam(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IStream* s;

	// Avoid null pointers for callers not checking errors
	if (((r->r[1] > 2) && (r->r[1] <= EMeta_MaxValue))
	||  (r->r[1] == EMeta_StreamMimeType))
		r->r[2] = (int) &nil;

	e = IStream_GetReadyHeader(g, r->r[0], &s);
	if (e) return e;

	switch (r->r[1])
	{
		case -1: // Global timestamp
		{
			r->r[3] = s->info.TimeStamp;
		}
		break;
		case 0: // Channels
		{
			r->r[2] = s->info.Channels;
		}
		break;
		case 1: // Sample Rate
		{
			r->r[2] = s->info.SampleRate;
		}
		break;
		case 2: // Mean Bitrate
		{
			r->r[2] = s->info.MeanBitRate;
		}
		break;
		case EMeta_StreamFileType: // RISC OS filetype
		{
			r->r[2] = s->source.os_filetype;
			r->r[3] = s->info.TimeStamp;
		}
		break;
		case EMeta_StreamMimeType: // mime type
		{
			r->r[2] = (int)(s->source.mimetype ? s->source.mimetype : &nil);
			r->r[3] = s->info.TimeStamp;
		}
		break;
		case EMeta_StreamTrackNumber:
		{
			Meta* meta = IStream_FindMetadata(s, r->r[1]);
			r->r[2] = (meta && meta->data) ? *(int*)meta->data : 0;
			r->r[3] = (int)( meta ? meta->timestamp : 0);
		}
		break;
		default:
		{
			if ((r->r[1] > 2) && (r->r[1] <= EMeta_MaxValue))
			{
				Meta* meta = IStream_FindMetadata(s, r->r[1]);
				r->r[2] = (int)( (meta && meta->data) ? meta->data : &nil);
				r->r[3] = (int)( meta ? meta->timestamp : 0);
			}
			else return &Err_UnknownParamCode;
		}
	}

	return NULL;
}

/*---------------------
 * SWI StreamDecoding
 *
 * In  - R0  handle
 *       R1  code
 *       R2  0 or 64-bit buffer
 *
 * Out - R2...
 */

const _kernel_oserror* swi_StreamDecoding(GlobHdr* g, _kernel_swi_regs* r)
{
	const const _kernel_oserror* e;
	IStream* s;
	int64_t* ppos = (int64_t*) r->r[2];
	int64_t pos = 0;
	int	ready = 1;
	int ioff;

	e = IStream_GetReadyHeader(g, r->r[0], &s);
	if (e) ready = 0;
	e = IStream_GetHeader(g, r->r[0], &s);
	if (e) return e;
	if (s->state == EIStream_State_FatalError)
		return &s->LastError;

	if (!s->codec.fn) ready = 0;

	// do not use return till irqs are on again
	ioff = _kernel_irqs_disabled();
	if (!ioff) _kernel_irqs_off();

	switch (r->r[1])
	{
		case 0: // output buffer filled size in ms
		{
			if (ready && s->info.SampleRate)
			{
				int size, filled;
				e = s->codec.fn->BufferSizes(s, &size, &filled);
				if (!e)
				{
					pos = (int64_t) filled;
					pos *= 1000;
					pos /= s->info.SampleRate;
				}
			}
		}
		break;
		case 1: // output buffer total size in ms
		{
			if (ready && s->info.SampleRate)
			{
				int size, filled;
				e = s->codec.fn->BufferSizes(s, &size, &filled);
				if (!e)
				{
					pos = (int64_t) size;
					pos *= 1000;
					pos /= s->info.SampleRate;
				}
			}
		}
		break;
		case 2: // total playing time in ms
		{
			pos = s->info.PlayingTime / 256000;
		}
		break;
		case 3: // input buffer filled size in bytes
		{
			int filled = s->inb->free - s->inb->start;
			if (filled < 0) filled += s->inb->size;

			pos = (int64_t) filled;
		}
		break;
		case 4: // input buffer total size in bytes
		{
			pos = (int64_t) s->inb->size;
		}
		break;
		case 5: // total amount of data read in bytes
		{
			pos = s->source.BytesRead;
		}
		break;
		case 6: // current position in ms
		{
			if (ready)
				e = s->codec.fn->ReadPos(s, &pos);
		}
		break;
		case 7: // file duration in ms
		{
			if (ready)
				e = s->codec.fn->ReadDuration(s, &pos);
		}
		break;
		case 8: // guessed total playing time including what's left to play in buffers (output + input) in ms
		{
			pos = s->info.PlayingTime / 256000;

			if (ready && s->info.SampleRate && s->info.MeanBitRate)
			{
				int64_t tmp;
				int size, filled;
				e = s->codec.fn->BufferSizes(s, &size, &filled);
				if (!e)
				{
					tmp = (int64_t) filled;
					tmp *= 1000;
					tmp /= s->info.SampleRate;
					pos += tmp;

					filled = s->inb->free - s->inb->start;
					if (filled < 0) filled += s->inb->size;
					tmp = (int64_t) filled;
					tmp *= 8;
					tmp /= s->info.MeanBitRate;

					pos += tmp;
				}
			}
		}
		break;
		case 9: // guessed total remaining time including what's left to play in buffers (output + input) in ms
		{
			pos = 0;

			if (ready && s->info.SampleRate && s->info.MeanBitRate)
			{
				int64_t tmp;
				int size, filled;
				e = s->codec.fn->BufferSizes(s, &size, &filled);
				if (!e)
				{
					tmp = (int64_t) filled;
					tmp *= 1000;
					tmp /= s->info.SampleRate;
					pos += tmp;

					filled = s->inb->free - s->inb->start;
					if (filled < 0) filled += s->inb->size;
					tmp = (int64_t) filled;
					tmp *= 8;
					tmp /= s->info.MeanBitRate;

					pos += tmp;
				}
			}
		}
		break;

		default:
			e = &Err_UnknownParamCode;
	}

	if (!ioff) _kernel_irqs_on();

	if (!e) *ppos = pos;

	return e;
}

/**
 * Reads playback parameters on a given channel of a stream.
 * In:
 *  r[0] = stream id.
 *  r[1] = channel nr [0, nr of channels[
 *  r[2] = 16-bit buffer ptr where to copy sound output on this channel or 0 if not used
 *  r[3] = length of buffer (in bytes)
 * Out:
 *  r[3] = frequency
 *  r[4] = if r[2] != 0 on input
 *             buffer peak value [0, &7fff]
 *          else
 *             note volume [0, &10000]
 *  r[5] = if r[2] != 0 on input
 *             buffer mean value [0, &7fff]
 *          else
 *             note volume [0, &10000]
 *  r[6] = panning [0 (left), 256 (right)], 384 for surround
 */
const _kernel_oserror* swi_ChannelParams(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IStream* s;
	MixStream* pMixStram;

	e = IStream_GetReadyHeader(g, r->r[0], &s);
	if (e) return e;

	if ((r->r[1] < 0) || (r->r[1] >= s->info.Channels))
		return &Err_InvalidChannelNr;

	pMixStram = &s->ChannelsArray[r->r[1]];

#ifndef MAKEABS
	int ioff = _kernel_irqs_disabled();
	if (!ioff) _kernel_irqs_off();

	if (pMixStram->pos != -1)
	{
		if (r->r[2] && (r->r[3] > 0))
		{
			Channel_Monitor nm = {g, pMixStram, (void*) r->r[2], r->r[3]};

			Channel_FillMonitorBuffer(&nm);
			r->r[4] = nm.peekVolume;
			r->r[5] = nm.meanVolume;
		}
		else
			r->r[5] = r->r[4] = 1<<16;
	}
	else
	{
		if (r->r[2]) memset((void*) r->r[2], 0, r->r[3]);
		r->r[4] = 0;
		r->r[5] = 0;
	}
	r->r[3] = pMixStram->frequency;
	r->r[6] = pMixStram->panning;

	if (!ioff) _kernel_irqs_on();
#endif

	return NULL;
}

/**
 * SWI ChannelStatus
 *
 * In:
 *   r[0] stream handle
 *   r[1] channel nr [0, nr of channels[
 *   r[2] actions
 *            channel discribed in r[1]
 *              bit 0: alter status: 1 yes, 0 no
 *              bit 1: new status: 1 play, 0 mute
 *            channels other than the one discribed in R1
 *              bit 2: alter status: 1 yes, 0 no
 *              bit 3: new status: 1 play, 0 mute
 * Out:
 *   r[3] 0 mute, 1 play
 */
const _kernel_oserror* swi_ChannelStatus(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* e;
	IStream* s;
	int ch;

	e = IStream_GetReadyHeader(g, r->r[0], &s);
	if (e) return e;

	ch = r->r[1];
	if ((ch < 0) || (ch >= s->info.Channels))
		return &Err_InvalidChannelNr;

	uint32_t chm = s->info.ChannelsMuting & (1 << ch);
	uint32_t cho = s->info.ChannelsMuting & ~(1 << ch);

	// Treat the other channels
	if (r->r[2] & 4)
	{
		if (r->r[2] & 8)
			cho = 0;
		else
			cho = ~(1 << ch);
	}

	// Treat the mentionned channel
	if (r->r[2] & 1)
	{
		if (r->r[2] & 2)
			chm = 0;
		else
			chm = 1 << ch;
	}
	s->info.ChannelsMuting = chm | cho;

	if (s->info.ChannelsMuting & (1 << ch))
		r->r[3] = 0;
	else
		r->r[3] = 1;

	return NULL;
}

/**
 * SWI GetMixingInfo
 *
 * In:
 * Out:
 *   r[0] CPU usage in 1/1000
 *   r[1] Max CPU value encountred so far
 *   r[2] Number of sample mixed
 *   r[3] Max number of sample mixed so far
 *   r[4] Max number of sample limit
 */
#ifndef MAKEABS
const _kernel_oserror* swi_GetMixingInfo(GlobHdr* g, _kernel_swi_regs* r)
{
	const _kernel_oserror* err = NULL;

	err = CSeq_GetMixingInfo(g, r);
	if (err) return err;

	int cpu = 0;
	for(int i = 0; i < CPU_SLOTS; i++)
		cpu += g->cpu[i];
	cpu /= CPU_SLOTS;

	r->r[0] += cpu;
	// Not true but consider max cpu usage has max of mixing + max of callnack
	if (g->cpu_max < (r->r[1] + cpu))
		g->cpu_max = r->r[1] + cpu;
	r->r[1] = g->cpu_max;

	return NULL;
}
#endif

/*------------------------------------------------------------------------------
 * Stream background/interrupts routines, all called in SVC mode
 */

static const _kernel_oserror* IStream_Process(IStream* s)
{
	const _kernel_oserror* e;

	if (s->codec.fn->ExtProcess)
	{
		e = s->codec.fn->ExtProcess(s);
		return e;
	}

	int ioff = _kernel_irqs_disabled();

	if (!s->codec.fn->Process)
		return NULL;

	// Copy buffers info so that we are not bothered by interrupts
	if (!ioff) _kernel_irqs_off();
	s->bitb.start = s->bitb.data + s->inb->start;
	if (s->bitb.start < s->bitb.data) s->bitb.start = s->bitb.data;
	if (s->bitb.start > s->bitb.last) s->bitb.start = s->bitb.last;
	s->bitb.free  = s->bitb.data + s->inb->free;
	if (s->bitb.free < s->bitb.data) s->bitb.free = s->bitb.data;
	if (s->bitb.free > s->bitb.last) s->bitb.free = s->bitb.last;
	if (!ioff) _kernel_irqs_on();

	e = s->codec.fn->Process(s);

	// Update buffers
	if (!ioff) _kernel_irqs_off();
	s->inb->start = s->bitb.start - s->bitb.data;
	if (!ioff) _kernel_irqs_on();

	return e;
}

static void IStreams_BackgroundEnd(GlobHdr* g)
{
	int i;

	for (i = 0; i < max_streams; i++)
	{
		IStream* s = g->streamsptr[i];

		// If no input defined or fatal error, do nothing
		if (!s
		|| !s->reader.fn
		||  (s->state == EIStream_State_NoSource) // Open not completed
		||  (s->state == EIStream_State_FatalError))
		{
			// If no input defined or fatal error, do nothing
		}
		else
		{
			//--------------------------------------
			// Treat end of (sub-)stream conditions
			//--------------------------------------

			if (s->status & swrk_status_playing)
			{
				if ((s->status & swrk_status_noloop)
				||  !IStream_IsSeekable(s)
#ifdef MAKEABS
		        ||  1
#else
				||  (s->writer.fn != &ToSpeakers)
#endif
				)
				{
					// Stream entirely played? Stop playing
					if (s->status & swrk_status_played)
					{
						if (s->chained_handle != -1)
						{
							_kernel_swi_regs r;

							r.r[0] = s->chained_handle;
							swi_StreamPlay(g, &r);
						}

						int ioff = _kernel_irqs_disabled();
						if (!ioff) _kernel_irqs_off();

						s->status &= ~swrk_status_playing;
						s->writer.fn->Close(s);

						if (!ioff) _kernel_irqs_on();
					}
					else if (s->status & swrk_status_decoded)
					{
						int remain = s->codec.fn->Lister(s);

						if (remain < 1024) // ~= 1/50 Hz at 44KHz
						{
							if (s->chained_handle != -1)
							{
								_kernel_swi_regs r;

								r.r[0] = s->chained_handle;
								swi_StreamPlay(g, &r);
							}
						}
					}
				}
				else
				{
					// Stream entirely decoded? Move back to start of file
					if (s->status & swrk_status_decoded)
					{
						s->status |= swrk_status_RD_restart;
					}
				}
			}
		}
	}
}

void IStream_BackgroundProcess(GlobHdr* g, IStream* s)
{
	const _kernel_oserror* e;
	int filled;

	//--------------------------------------
	// Treat end of (sub-)stream conditions
	// of all streams to try to reduce gaps
	// in chained streams
	//--------------------------------------
	IStreams_BackgroundEnd(g);

	// If no input defined or fatal error, do nothing
	if (!s->reader.fn
	||  (s->state == EIStream_State_NoSource) // Open not completed
	||  (s->state == EIStream_State_FatalError))
		return;

	//-------------------------
	// Reading of input stream
	//-------------------------

	// Save buffers state if controller is external
	if (s->reader.fn == &FromDesc);
		IStream_SaveInb(s);

	// Check buffers are still correct
	if (IStream_CheckIntegrity(s))
		return;

	// Check if input stream may be accessed
	if (!s->reader.fn->DelayIO(s))
	{
		s->status |= swrk_status_RD_nodelay;

		if (s->status & swrk_status_RD_restart)
		{
			// Reset chunk size to large size
			s->reader.chunksize = (s->inb->size >> 1);

			// Clear input buffers
			e = s->codec.fn->ClearInput(s);
			if (e) goto error;

			IStream_SaveInb(s);

			// Move to start of file
			e = IStream_Seek(s, 0);
			if (e) goto error;

			s->status &= ~(swrk_status_RD_restart
			          |swrk_status_decoded    |swrk_status_played
		              |swrk_status_sub_decoded|swrk_status_sub_played
		              );
		}

		// Check that buffer remains somewhat filled
		e = s->reader.fn->Fill(s);
		if (e) goto error;
#ifndef MAKEABS
		// Reset buffer reading to more normal settings after first buffer
		if (s->info.chunksize) s->reader.chunksize = s->info.chunksize;
#endif

		// Add difference to Read bytes
		filled = s->inb->free - s->saved_inb.free;
		if (filled < 0) filled += s->saved_inb.size;
		s->source.BytesRead += filled;

		// Save buffers state
		IStream_SaveInb(s);
	}
	else
	{
		s->status &= ~swrk_status_RD_nodelay;
	}

	// Loop till a return occurs
	while(1)
	{
		switch(s->state)
		{
			case EIStream_State_MetaDataLookup:
			{
				int b;

#ifndef MAKEABS
				if (s->writer.fn == &ToSrcFile)
				{
					s->state = EIStream_State_InputSaving;
					s->status |= swrk_status_ready;
					continue;
				}
#endif

				// Process meta headers
				e = IStream_ProcessMeta(s, &b);
				if (e) goto error;

				// More meta to process later?
				if (b) return;

				s->state = EIStream_State_CodecLookup;
			}
			break;
			case EIStream_State_CodecLookup:
			{
				int chunksize;

				// Ensure there is a matching codec
				e = IStream_GetCodec(s);
				if (e) goto error;

				// Round chunk size
				chunksize = 64*1024;
				while (chunksize < s->info.chunksize)
					chunksize <<= 1;
				if (chunksize > (s->inb->size >> 2))
					s->info.chunksize = (s->inb->size >> 2);
				else
					s->info.chunksize = chunksize;

				// Find out is codec is ready
				s->state = EIStream_State_CodecInit;
			}
			break;
			case EIStream_State_CodecInit:
			{
				// Don't do anything while IO are delayed.
				if (s->status & swrk_status_RD_nodelay)
				{
					bool bready;

					e = IStream_Process(s);
					if (e) goto error;

					e = s->codec.fn->IsReady(s, &bready);
					if (e) goto error;

					if (bready)
					{
						// Ready to play
						s->status |= (swrk_status_ready | swrk_status_buffering);
						s->status &= ~swrk_status_sub_init;

						s->writer.fn->SetParams(s);

						s->state = EIStream_State_Buffering;
						continue;
					}
				}

				// Continue later
				return;
			}
			break;
			case EIStream_State_Buffering:
			{
				int size, filled;

				e = IStream_Process(s);
				if (e) goto error;

				if (((s->status & swrk_status_buffering) == 0)
				||  IStream_IsSeekable(s))
				{
					// Ready to play
					s->status &= ~swrk_status_buffering;
					s->state = EIStream_State_CodecReady;
					continue;
				}

				e = s->codec.fn->BufferSizes(s, &size, &filled);
				if (e) goto error;

				filled <<= 1; // just a trick for simplifying the tests
				if ((filled > (g->cfg.buffering*s->info.SampleRate))
				||  (filled > size))
				{
					// Ready to play
					s->status &= ~swrk_status_buffering;
					s->state = EIStream_State_CodecReady;
					continue;
				}

				// Continue later
				return;
			}
			break;
			case EIStream_State_CodecReady:
			{
				if (s->status & swrk_status_buffering)
				{
					s->state = EIStream_State_Buffering;
					continue;
				}

				if (s->status & swrk_status_sub_init)
				{
					s->status &= ~(swrk_status_ready | swrk_status_decoded | swrk_status_played);
					s->state = EIStream_State_CodecInit;
					continue;
				}

				e = IStream_Process(s);
				if (e) goto error;

				// Call output filler
				e = s->writer.fn->Fill(s);
				if (e) goto error;

				// Don't do anything
				return;
			}
			break;
			case EIStream_State_InputSaving:
			{
				// Call output filler
				if (s->status & swrk_status_playing)
				{
					e = s->writer.fn->Fill(s);
					if (e) goto error;
				}

				return;
			}
			break;
			default:
			{
				// Don't do anything
				return;
			}
		}
	}

error:
	s->LastError = *e;
	s->state = EIStream_State_FatalError;
	Log("Codec in error: %s", &s->LastError.errmess[0]);
}
