/*  SD_MIX.C

by yzi/fit 1997

versio B.11




changes: 

	2003-06-06: tästä pitäis vääntää nyt jotain
	vähän mielenkiintoisempaa kuin pelkkä mod-playeri.
	PT.C olkoon soittorutiinina, mutta sen perään laitetaan
	jotain filtteriä ja muuta vatkutinta.

	Siivosin kaikki asm-kököt ynnä muut tarpeettomat koodit pois.


         11.05.1997
	 0.11 - mixing functions in assembler (SD_AMIX.ASM)
	      - C versions also available (#ifndef MIX_ASM)

	 08.05.1997
	 0.10 - now this finally works

	 29.04.1997
	 0.01 - everything is new



Kaikille miksaaville laitteille yhteiset rutiinit.
SD_MIX on tavallaan kuin GF1, ja muut moduulit
antavat sille pääsyn ulkomaailmaan. (esim. SD_SB16)

"Sample" koostuu muutamasta asiasta:
	- pointeri dataan, koko
	- looppitiedot
	- 16bit juu/ei


Kanavatietoja on kahdet: todelliset ja työn alla olevat
kanavat. Kun miksaus alkaa, kopioidaan todellisiin kanaviin
uudet arvot. (Jos muutellaan arvoja kesken miksauksen niin
ei mene sekaisin...)

(HUOM! Edellä mainittu pätee vain 2-keskeytysjärjestelmiin,
joissa tosiaan musiikinsoittorutiini voi kutsua mix-rutiineja
koska vain. Jos homma tehdään vuoron perään miksaus/soitto,
ei ongelmia tietenkään synny.

*/

#include <stdlib.h>
#include <math.h>
#include <string.h>
#include "sd_main.h"
#include "sd_mix.h"

#define REALLY_SIMPLE

// the dithering code hasn't been tested in a long time
#define NO_DITHERING_PLEASE

#define MAX_EFFECTS 2
#define MAX_INPUT_BUSES 2

// viimeinen on masterväylä
#define MASTER_BUS (SD_MAXBUSES-1)

// if you want the internal signal buses to be integer (for machines without FPUs), define this
#define SAMPLETYPE_INT

#ifdef SAMPLETYPE_INT
typedef int sample_type;
#define VOLUMETYPE_INT
#else
typedef float sample_type;
#endif

#ifdef VOLUMETYPE_INT
typedef int volume_type;
#else
typedef float volume_type;
#endif


struct BUS_INPUT {
	struct BUS *bus;
	volume_type level;
};

/* väylä/"kanava" */
struct BUS {
	sample_type *buffer;
	int blocksize; /* bufferin koko */
	int channels; /* montako kanavaa (1=mono, 2=stereo) */
	struct BUS_INPUT inputs[MAX_INPUT_BUSES]; /* lisäinputit, jotka väylään summataan */
	void *effects[MAX_EFFECTS]; /* insert-efektit, sarjassa */
};

void mix_bus_init(struct BUS *bus, int blocksize, int channels)
{
	int i;

	bus->buffer = malloc(blocksize * sizeof(sample_type) * channels); /* stereo! */
	bus->blocksize = blocksize;
	bus->channels = channels;
 	for (i=0;i<MAX_EFFECTS;i++) bus->effects[i] = NULL;
 	for (i=0;i<MAX_INPUT_BUSES;i++) bus->inputs[i].bus = NULL;
} /* mix_bus_init */

void mix_bus_add_effect(struct BUS *bus, void *effect)
{
	int i;

	/* etsitään tyhjä slotti */
	i = 0;
	while (i<MAX_EFFECTS && bus->effects[i]) i++;
	if (i < MAX_EFFECTS) bus->effects[i] = effect;

	/* jos loppui tila niin sitten tila loppui */
} /* mix_bus_add_effect */

void mix_bus_add_input(struct BUS *bus, struct BUS *inputbus, volume_type level)
{
	int i;

	/* etsitään tyhjä slotti */
	i = 0;
	while (i<MAX_INPUT_BUSES && bus->inputs[i].bus) i++;
	if (i < MAX_INPUT_BUSES) {
		bus->inputs[i].bus = inputbus;
		bus->inputs[i].level = level;
	}

	/* jos loppui tila niin sitten tila loppui */
} /* mix_bus_add_input */

void mix_bus_clear(struct BUS *bus)
{
	memset(bus->buffer, 0, bus->blocksize * sizeof(sample_type) * bus->channels);
}


struct SAMPLEINFOBLOCK {
	signed char *data;
	unsigned int
	    size,
	    loopstart,
	    loopend,
	    looptype,
	    sample16bit;

	struct BUS *target_bus;
};

struct CHANNELINFOBLOCK {
	signed char *sampledata;
	unsigned int
	    sample,
	    volume,
            offsetlo, /* offset samplen alusta */
	    offsethi,
	    deltalo,
	    deltahi,
	    loopstart,
	    loopend,
	    looptype,
	    samplesize,
	    panning,
	    sample16bit,
	    newdelta, /* Jos 1, asetetaan ko. arvo. hämärää, mutta toimii */
	    newoffset; /* Jos 1, asetetaan ko. arvo. hämärää mutta toimii */
};

int sd_mix_interpolation=1;
int sd_mix_rate=44100;

struct SAMPLEINFOBLOCK mix_samples[SD_MAXSAMPLES];

static signed int	mix_curchannel=0,
		mix_mainvolume=SD_MAXMAINVOLUME,
		mix_activechannels=4;


static struct CHANNELINFOBLOCK	mix_channels[SD_MAXCHANNELS],
				mix_realchannels[SD_MAXCHANNELS],
				*curchan;

static struct BUS mix_buses[SD_MAXBUSES];

int mix_getsample (int snum, signed char *src, int len,
		    int loopstart, int loopend, int looptype)
/* "Ottaa" samplen. Muistia ei varata tässä, joten olis ihan kiva,
   että se tila pysyisi varattuna. */
{
	mix_samples[snum].data = src;
	mix_samples[snum].size = len;
	mix_samples[snum].loopstart = loopstart;
	mix_samples[snum].loopend = loopend;
	mix_samples[snum].looptype = looptype;

	return SD_OK;
} /* mix_getsample */

void mix_selectchannel (int cnum)
{
	mix_curchannel = cnum;
	curchan = &(mix_channels[mix_curchannel]);
} /* mix_selectchannel */

void mix_setsample (int snum)
{
	curchan->sample = snum;
	curchan->sampledata = mix_samples[snum].data;
	curchan->loopstart = mix_samples[snum].loopstart;
	curchan->loopend = mix_samples[snum].loopend;
	curchan->looptype = mix_samples[snum].looptype;
	curchan->samplesize = mix_samples[snum].size;
} /* mix_setsample */

void mix_setamigaperiod (int period)
{
	double d, h, l;
	if(period!=0)
		d = ((double)AMIGA_MASTERCLOCK) / ((double)period * sd_mix_rate);
	else
		d = 0.0;

	l = modf(d, &h);

        //curchan->deltahi = (d-l);
	if(period!=0)
		curchan->deltahi=AMIGA_MASTERCLOCK/period/sd_mix_rate;
	else
		curchan->deltahi=0;

	curchan->deltalo = (int)(l * (65536.0*256)) << 8;

	curchan->newdelta = 1;

} /* mix_setamigaperiod */

void mix_setvolume (int volume)
{
	curchan->volume = volume;
} /* mix_setvolume */

void mix_setpanning (int panning)
{
	curchan->panning = panning;
} /* mix_setpanning */

void mix_setoffset (int offset)
{
	if (offset > curchan->loopend)
	{ /* Loopin ulkopuolella - kytketään kanavan looppi pois. */
		curchan->looptype = LOOP_OFF;
	}
	if (offset < curchan->samplesize)
	{
		curchan->offsethi = offset;
		curchan->offsetlo = 0;
		curchan->newoffset = 1;
	}
	/* Jos on järjetön arvo niin ei tehdä mitään. */
} /* mix_setoffset */

void mix_setmainvolume (int volume)
{
	if (volume > SD_MAXMAINVOLUME*4) volume = SD_MAXMAINVOLUME*4;
	mix_mainvolume = volume;
} /* mix_setmainvolume */

void mix_setactivechannels (int activechannels)
{
	mix_activechannels = activechannels;
} /* mix_setactivechannels */

void sd_mix_setfunctions ()
{
	/* Asetetaan sd-funktiot */
	sd_getsample = mix_getsample;
	sd_selectchannel = mix_selectchannel;
	sd_setsample = mix_setsample;
	sd_setamigaperiod = mix_setamigaperiod;
	sd_setvolume = mix_setvolume;
	sd_setpanning = mix_setpanning;
	sd_setoffset = mix_setoffset;
	sd_setmainvolume = mix_setmainvolume;
	sd_setactivechannels = mix_setactivechannels;
} /* sd_mix_setfunctions */


void sd_mix_initchannel (struct CHANNELINFOBLOCK *c)
{
	c->volume = 0;
	c->offsetlo = 0;
	c->offsethi = 0;
	c->deltahi = 0;
	c->deltalo = 0;
	c->loopstart = 0;
	c->loopend = 0;
	c->looptype = LOOP_OFF;
	c->panning = PAN_MIDDLE;
	c->sampledata = (signed char *)0; /* NULL */
	c->sample = 0;
	c->sample16bit = 0;
	c->newoffset = 1;
	c->newdelta = 1;
} /* sd_mix_initchannel */

void sd_mix_initsample (struct SAMPLEINFOBLOCK *s)
{
	s->loopstart = 0;
	s->loopend = 0;
	s->looptype = LOOP_OFF;
	s->data = (signed char *)0; /* NULL */

	s->target_bus = &mix_buses[0];
} /* sd_mix_initchannel */

void sd_mix_init (int rate, int blocksize)
{
	int	i;

	sd_mix_rate = rate;

	for (i=0; i<SD_MAXBUSES; i++)
	{
		mix_bus_init(&mix_buses[i], blocksize, 2);
	}


	for (i=0; i<SD_MAXCHANNELS; i++)
	{
		sd_mix_initchannel(&mix_channels[i]);
		sd_mix_initchannel(&mix_realchannels[i]);
	}

	for (i=0; i<SD_MAXSAMPLES; i++)
	{
		sd_mix_initsample (&mix_samples[i]);
	}

        for(i=0;i<SD_MAXSAMPLES;i++)
	    mix_samples[i].target_bus = &mix_buses[0];

#ifdef VOLUMETYPE_INT
	mix_bus_add_input(&mix_buses[MASTER_BUS], &mix_buses[0], 128);
#else
	mix_bus_add_input(&mix_buses[MASTER_BUS], &mix_buses[0], 0.5);
#endif

} /* sd_mix_init */

/* ---------------------- VARSINAINEN MIKSAUS -------------------- */

void long_addition (unsigned int *h,
                    unsigned int *l,
                    unsigned int *dh,
                    unsigned int *dl);

void long_addition (unsigned int *h,
                    unsigned int *l,
                    unsigned int *dh,
                    unsigned int *dl)
{
        static unsigned int i;

        (*h) += (*dh);
        i = (*l);
        if (((*l)+=(*dl))<i) (*h)++;
} /* long_addition */


void mix_copychannel(struct CHANNELINFOBLOCK *dest,
		     struct CHANNELINFOBLOCK *src)
/* Kopioi kanavan muut tiedot paitsi offsetin. */
{
	unsigned int saveofshi, saveofslo,
		     savedeltahi, savedeltalo;

	saveofshi = dest->offsethi;
	saveofslo = dest->offsetlo;
	savedeltahi = dest->deltahi;
	savedeltalo = dest->deltalo;


	/* Tässä on hyvin riskialtis paikka... voi mennä väärin. Ihan tosi. */

	*dest = *src; /* kopioidaan kanavat */
	if (!(src->newoffset))
	{ /* Ei laitettukaan sitä offsetia */
		dest->offsethi = saveofshi;
		dest->offsetlo = saveofslo;
//                src->newoffset = 0; /* NewOffset-lippu alas. */
	}
	else src->newoffset = 0; /* NewDelta-lippu alas. */
	if (!(src->newdelta))
	{ /* Ei laitettukaan sitä offsetia */
		dest->deltahi = savedeltahi;
		dest->deltalo = savedeltalo;
	}
	else src->newdelta = 0; /* New-lippu alas. */
} /* mix_copychannel */



/* Interpoloiva stereomiksaaja */
void sd_c_imixstereo (struct CHANNELINFOBLOCK *chan, sample_type *dst32, int length, volume_type lvol, volume_type rvol)
{
        static int i;
		static sample_type left, right;

        if (chan->looptype)
        for (i=0; i<length; i++)
        {

                long_addition (&(chan->offsethi), &(chan->offsetlo),
                               &(chan->deltahi), &(chan->deltalo));

                if (chan->offsethi > chan->loopend)
                {
                        chan->offsethi = chan->loopstart+1;
                }



#ifdef SAMPLETYPE_INT
                left = ((int)(chan->sampledata[(chan->offsethi)+1])
					* (chan->offsetlo >> 16))/65536;
				left += ((int)(chan->sampledata[(chan->offsethi)])
					* (65536 - (chan->offsetlo >> 16)))/65536;
				right = (left * rvol) / 65536;
				left = (left * lvol) / 65536;
#else
                left = (((float)(chan->sampledata[(chan->offsethi)+1])) / 128.0) 
					* (float)(chan->offsetlo >> 16)/65536.0;
				left += (((float)(chan->sampledata[(chan->offsethi)])) / 128.0) 
					* (1.0 - (float)(chan->offsetlo >> 16)/65536.0);
				right = left * rvol;
				left = left * lvol;
#endif
                *(dst32++) += left;
                *(dst32++) += right;
        } /* for i */
        else
        for (i=0; i<length; i++)
        {

                long_addition (&(chan->offsethi), &(chan->offsetlo),
                               &(chan->deltahi), &(chan->deltalo));

                if (chan->offsethi >= chan->samplesize)
                {
                        chan->offsethi = 0; //chan->samplesize;
                        chan->deltahi = 0;
                        chan->deltalo = 0;
                        chan->volume = 0;
                        break;
                }

#ifdef SAMPLETYPE_INT
                left = ((int)(chan->sampledata[(chan->offsethi)+1])
					* (chan->offsetlo >> 16))/65536;
				left += ((int)(chan->sampledata[(chan->offsethi)])
					* (65536 - (chan->offsetlo >> 16)))/65536;
				right = (left * rvol) / 65536;
				left = (left * lvol) / 65536;
#else
                left = (((float)(chan->sampledata[(chan->offsethi)+1])) / 128.0) 
					* (float)(chan->offsetlo >> 16)/65536.0;
				left += (((float)(chan->sampledata[(chan->offsethi)])) / 128.0) 
					* (1.0 - (float)(chan->offsetlo >> 16)/65536.0);
				right = left * rvol;
				left = left * lvol; 
#endif

                *(dst32++) += left;
                *(dst32++) += right;
        } /* for i */
} /* sd_c_imixstereo */

/* Interpoloiva stereomiksaaja */
void sd_c_imixstereo_tos16i(struct CHANNELINFOBLOCK *chan, signed short *dst16, int length, volume_type lvol, volume_type rvol)
{
        static int i;
	static signed int left;

        if (chan->looptype)
        for (i=0; i<length; i++)
        {

                long_addition (&(chan->offsethi), &(chan->offsetlo),
                               &(chan->deltahi), &(chan->deltalo));

                if (chan->offsethi > chan->loopend)
                {
                        chan->offsethi = chan->loopstart+1;
                }

                left = (signed int)(chan->sampledata[(chan->offsethi)]);
		if (dst16)
		{
			*(dst16++) += (signed short)(left * rvol);
			*(dst16++) += (signed short)(left * lvol);
		}

/*
                left = ((int)(chan->sampledata[(chan->offsethi)+1])
					* (chan->offsetlo >> 16))/65536;
		left += ((int)(chan->sampledata[(chan->offsethi)])
				* (65536 - (chan->offsetlo >> 16)))/65536;
		right = (left * rvol) / 256;
		left = (left * lvol) / 256;
		
		if (left > 32767) left = 32767;
		else if (left < -32768) left = -32768;
		if (right > 32767) right = 32767;
		else if (right < -32768) right = -32768;
		
                *(dst16++) += (signed short)left;
                *(dst16++) += (signed short)right; */
        } /* for i */
        else
        for (i=0; i<length; i++)
        {

                long_addition (&(chan->offsethi), &(chan->offsetlo),
                               &(chan->deltahi), &(chan->deltalo));

                if (chan->offsethi >= chan->samplesize)
                {
                        chan->offsethi = 0; //chan->samplesize;
                        chan->deltahi = 0;
                        chan->deltalo = 0;
                        chan->volume = 0;
                        break;
                }

                left = (signed int)(chan->sampledata[(chan->offsethi)]);
		if (dst16)
		{
			*(dst16++) += (signed short)(left * rvol);
			*(dst16++) += (signed short)(left * lvol);
		}

/*                left = ((int)(chan->sampledata[(chan->offsethi)+1])
					* (chan->offsetlo >> 16))/65536;
		left += ((int)(chan->sampledata[(chan->offsethi)])
			* (65536 - (chan->offsetlo >> 16)))/65536;
		right = (left * rvol) / 256;
		left = (left * lvol) / 256;
	
		if (left > 32767) left = 32767;
		else if (left < -32768) left = -32768;
		if (right > 32767) right = 32767;
		else if (right < -32768) right = -32768;
		
                *(dst16++) += (signed short)left;
                *(dst16++) += (signed short)right; */
        } /* for i */
} /* sd_c_imixstereo_tos16i */

/* Konvertointirutiinit. Konvertoidaan sisäinen 32-bittinen
   äänen esitysmuoto toiseen muotoon (8/16bit, stereo/mono, signed/unsigned) */

void sd_c_stereo32ftos16i (sample_type *dst32, signed short *dst, int length)
{
    static int i;

#ifndef NO_DITHERING_PLEASE

#define BITS 16
#define RAND_MAX 0x7fff

/* dithering code by Paul Kellet / from musicdsp.org archives */

  int   r1, r2;                //rectangular-PDF random numbers
  float s1, s2;                //error feedback buffers
  float s = 0.5f;              //set to 0.0f for no noise shaping
  float w = pow(2.0,BITS-1);   //word length (usually bits=16)
  float wi= 1.0f/w;
  float d = wi / RAND_MAX;     //dither amplitude (2 lsb)
  float o = wi * 0.5f;         //remove dc offset
  float tmp;
#endif
  sample_type in;
  int   out;


    for (i=0; i<(length*2); i++)
	{
	        in = *(dst32++);

		/* hard clip */
#ifdef SAMPLETYPE_INT
		if (in > 32767) in = 32767; 
		if (in < -32768) in = -32768;
#else		
		if (in < -0.9999) in = -0.9999;
		if (in > 0.9999) in = 0.9999;
#endif

#ifndef NO_DITHERING_PLEASE

		r2=r1;                               //can make HP-TRI dither by
		r1=rand();                           //subtracting previous rand()

		in += s * (s1 + s1 - s2);            //error feedback
		tmp = in + o + d * (float)(r1 - r2); //dc offset and dither

		out = (int)(w * tmp);                //truncate downwards
		if(tmp<0.0f) out--;                  //this is faster than floor()

		s2 = s1;
		s1 = in - wi * (float)out;           //error
#else
  #ifdef SAMPLETYPE_INT
		out = in;
  #else
		out = in * 32767;
  #endif
#endif
		/* final hard clip */

		if (out > 32767) out = 32767; 
		if (out < -32768) out = -32768;

		if (dst)
			*(dst++) = (signed short)out;
	} /* for i, klippaus */
} /* sd_c_stereo32ftos16i */


void sd_mix_stereo (signed short *dest, int length, int to16bit, int time)
{
#ifdef REALLY_SIMPLE
	signed int c;
	struct CHANNELINFOBLOCK *chan;
	static int lvol, rvol;

	for (c=0; c<mix_activechannels; c++)
		mix_copychannel(&mix_realchannels[c],
				&mix_channels[c]);
	
	/* Add MOD channels to target buses */
	for (c=0; c<mix_activechannels; c++)
	{
		chan = &mix_realchannels[c];

//		lvol = (chan->volume*(255-chan->panning))/128;
//		rvol = (chan->volume*(chan->panning))/128;
		lvol = (chan->volume*(255-chan->panning)*mix_mainvolume)/(SD_MAXMAINVOLUME) / 128;
		rvol = (chan->volume*(chan->panning)*mix_mainvolume)/(SD_MAXMAINVOLUME) / 128;

		if ((chan->samplesize>0) && (chan->volume>0))
                        sd_c_imixstereo_tos16i(chan, dest, length, lvol, rvol);

	} /* for channel */
                
	
#else
	sample_type *dst32, *src32;
	signed int i, ii, c, b;
	static volume_type lvol, rvol;
	signed int left, right;
	signed short *dst;
	struct SAMPLEINFOBLOCK *sample;
	struct CHANNELINFOBLOCK *chan;
	struct BUS *bus;
	struct SD_BASE_EFFECT *effect;

	/* Kopioidaan kanavat */
	for (i=0; i<mix_activechannels; i++)
		mix_copychannel(&mix_realchannels[i],
				&mix_channels[i]);

	/* Tyhjennetään linja-autot. */
	for (i=0; i<SD_MAXBUSES; i++)
		mix_bus_clear(&mix_buses[i]);

	/* Add MOD channels to target buses */
	for (c=0; c<mix_activechannels; c++)
	{
		chan = &mix_realchannels[c];
		sample = &mix_samples[chan->sample];
		dst32 = sample->target_bus->buffer;

#ifdef VOLUMETYPE_INT
		lvol = (chan->volume*(255-chan->panning)*mix_mainvolume)/(128*SD_MAXMAINVOLUME) / 128;
		rvol = (chan->volume*(chan->panning)*mix_mainvolume)/(128*SD_MAXMAINVOLUME) / 128;
		lvol = rvol = chan->volume;
#else
		lvol = (chan->volume*(255-chan->panning)*mix_mainvolume)/(128*SD_MAXMAINVOLUME) / 128.0;
		rvol = (chan->volume*(chan->panning)*mix_mainvolume)/(128*SD_MAXMAINVOLUME) / 128.0;
#endif
		if ((chan->samplesize>0) && (chan->volume>0))

                if (sd_mix_interpolation)
                        sd_c_imixstereo(chan, dst32, length, lvol, rvol);
                /* else
                        sd_c_mixstereo(chan, dst32, length, lvol, rvol); */		


	} /* for channel */


	/* process buses */
	for (b=0; b<SD_MAXBUSES; b++)
	{
		bus = &mix_buses[b];

		/* mix inputs to bus */
		for (ii=0;ii<MAX_INPUT_BUSES;ii++)
		{
			if (bus->inputs[ii].bus)
			{
				lvol = bus->inputs[ii].level;
				rvol = lvol;

				dst32 = bus->buffer;
				src32 = bus->inputs[ii].bus->buffer;		
				for (i=0; i<length; i++) 
				{
#ifdef VOLUMETYPE_INT
					// ADD VOLUMES HERE IF YOU WANT BUSSES
					*dst32++ += *src32++; /* left */
					*dst32++ += *src32++; /* right */
#else
					*dst32++ += *src32++ * lvol; /* left */
					*dst32++ += *src32++ * rvol; /* right */
#endif
				}
			}
		}

		/* now the inputs have been added, so we can apply effects */
		/*for (i=0; i<MAX_EFFECTS; i++)
			if (bus->effects[i])
			{
				effect=bus->effects[i];
				effect->process_replacing(effect, bus->buffer, bus->buffer, length, 2, time);
			}
*/
	}

	src32 = mix_buses[MASTER_BUS].buffer;
	dst = dest;

        if (to16bit)
                sd_c_stereo32ftos16i(src32, dst, length);
//	else
//		assert(0);
#endif
} /* sd_mix_stereo */


