/*
 --> Sample Loaders and Sample Processing

Name: sloader.c

Description:
Routines for loading samples.  The sample loader utilizes the routines
provided by the "registered" sample loader.  See SAMPLELOADER in
MIKMOD.H for the sample loader structure.

Portability:
All systems - all compilers

*/

#include "mikmod.h"


static int   sl_rlength;
static SWORD sl_old;
static SWORD *sl_buffer = NULL;
static SAMPLOAD *samplelist = NULL;



BOOL SL_Init(SAMPLOAD *s)        // returns 0 on error!
{
    if(sl_buffer==NULL)
         if((sl_buffer=(short *)_mm_malloc(4100)) == NULL) return 0;

    sl_rlength = s->length;
    sl_old = 0;
    return 1;
}


void SL_Exit(SAMPLOAD *s)
{
    if(sl_rlength > 0) _mm_fseek(s->fp,sl_rlength,SEEK_CUR);
}


void SL_Reset(void)
{
    sl_old = 0;
}


void SL_Load(void *buffer, SAMPLOAD *smp, int length)
{
    UBYTE infmt = smp->infmt, outfmt = smp->outfmt;
    SBYTE *bptr = (SBYTE *)buffer;
    SWORD *wptr = (SWORD *)buffer;
    int   stodo;
    int   t, u;

    // compute number of samples to load

    if(outfmt & SF_16BITS)
    {   length>>=1;
        sl_rlength>>=1;
    }

    while(length)
    {   stodo = (length<2048) ? length : 2048;

        if(infmt & SF_16BITS)
        {  if(infmt & SF_BIG_ENDIAN)
             _mm_read_M_SWORDS(sl_buffer,stodo,smp->fp);
           else
             _mm_read_I_SWORDS(sl_buffer,stodo,smp->fp);
        } else
        {   SBYTE  *s;
            SWORD  *d;

            fread(sl_buffer,sizeof(SBYTE),stodo,smp->fp);

            s  = (SBYTE *)sl_buffer;
            d  = sl_buffer;
            s += stodo;
            d += stodo;

            for(t=0; t<stodo; t++)
            {   s--;
                d--;
                *d = (*s)<<8;
            }
        }

        if(infmt & SF_DELTA)
        {   for(t=0; t<stodo; t++)
            {   sl_buffer[t] += sl_old;
                sl_old = sl_buffer[t];
            }
        }

        if((infmt^outfmt) & SF_SIGNED)
        {   for(t=0; t<stodo; t++)
               sl_buffer[t] ^= 0x8000;
        }

        if(smp->scalefactor)
        {   int   idx = 0;
            int   scaleval;

            // Sample Scaling... average values for better results.
            t = 0;
            while(t<stodo && length)
            {   scaleval = 0;
                for(u=smp->scalefactor; u && t<stodo; u--, t++)
                    scaleval += sl_buffer[t];
                sl_buffer[idx++] = scaleval / (smp->scalefactor-u);
                length--;
            }
            sl_rlength -= stodo;
            stodo = idx;
        } else
        {   length -= stodo;
            sl_rlength -= stodo;
        }

        if(outfmt & SF_16BITS)
        {   for(t=0; t<stodo; t++) *(wptr++) = sl_buffer[t];
        } else
        {   for(t=0; t<stodo; t++) *(bptr++) = sl_buffer[t]>>8;
        }
    }
}


SAMPLOAD *SL_RegisterSample(SAMPLE *s, FILE *fp)    // Returns 1 on error!
{
    SAMPLOAD *news, *cruise = samplelist;

    // Allocate and add structure to the END of the list

    if((news=(SAMPLOAD *)_mm_calloc(1,sizeof(SAMPLOAD))) == NULL) return NULL;

    if(cruise!=NULL)
    {   while(cruise->next!=NULL)  cruise = cruise->next;
        cruise->next = news;
    } else
        samplelist = news;

    news->infmt     = s->flags & 15;
    news->outfmt    = news->infmt;
    news->fp        = fp;
    news->sample    = s;
    news->length    = s->length;
    news->loopstart = s->loopstart;
    news->loopend   = s->loopend;

    return news;
}


static void FreeSampleList(void)
{
    SAMPLOAD *s = samplelist, *old;

    while(s!=NULL)
    {   old = s;
        s = s->next;
        free(old);
    }
    samplelist = NULL;
}


static ULONG SampleTotal(void)
// Returns the total amount of memory required by the samplelist queue.
{
    int       total = 0;
    SAMPLOAD  *s = samplelist;

    while(s!=NULL)
    {   s->sample->flags = (s->sample->flags&~15) | s->outfmt;
        total += MD_SampleLength(MD_MUSIC,s->sample);
        s = s->next;
    }

    return total;
}


static ULONG RealSpeed(SAMPLOAD *s)
{
    return(s->sample->c2spd / ((s->scalefactor==0) ? 1 : s->scalefactor));
}    


BOOL SL_LoadSamples(void)      // Returns 1 on error!
{
    SAMPLOAD  *c2smp;
    ULONG     maxsize, c2spd;
    SAMPLOAD  *s;

    // make sure the samples will fit inside availible RAM
    if((maxsize = MD_SampleSpace(MD_MUSIC)*1024) != 0)
    {   while(SampleTotal() > maxsize)
        {   // First Pass - check for any 16 bit samples
            s = samplelist;
            while(s!=NULL)
            {   if(s->outfmt & SF_16BITS)
                {   SL_Sample16to8(s);
                    break;
                }
                s = s->next;
            }
    
            // Second pass (if no 16bits found above) is to take the sample
            // with the highest c2spd and dither it by half.
            if(s==NULL)
            {   s = samplelist;
                c2spd = 0;
                while(s!=NULL)
                {   if((s->sample->length) && (RealSpeed(s) > c2spd))
                    {   c2spd = RealSpeed(s);
                        c2smp = s;
                    }
                    s = s->next;
                }
                SL_HalveSample(c2smp);
            }
        }
    }

    s = samplelist;
    while(s!=NULL)
    {   // sample has to be loaded ? -> increase number of
        // samples, allocate memory and load sample.

        if(s->sample->length)
        {   if(s->sample->seekpos)
               _mm_fseek(s->fp,s->sample->seekpos,SEEK_SET);

            // Call the sample load routine of the driver module.
            // It has to return a 'handle' (>=0) that identifies
            // the sample.

            s->sample->handle = MD_SampleLoad(s, s->fp);
            s->sample->flags  = (s->sample->flags&~15) | s->outfmt;
            if(s->sample->handle<0) return 1;
        }
        s = s->next;
    }

    FreeSampleList();
    return 0;
}


void SL_Sample16to8(SAMPLOAD *s)
{
    s->outfmt &= ~SF_16BITS;
    s->sample->flags = (s->sample->flags&~15) | s->outfmt;
}


void SL_Sample8to16(SAMPLOAD *s)
{
    s->outfmt |= SF_16BITS;
    s->sample->flags = (s->sample->flags&~15) | s->outfmt;
}


void SL_SampleSigned(SAMPLOAD *s)
{
    s->outfmt |= SF_SIGNED;
    s->sample->flags = (s->sample->flags&~15) | s->outfmt;
}


void SL_SampleUnsigned(SAMPLOAD *s)
{
    s->outfmt &= ~SF_SIGNED;
    s->sample->flags = (s->sample->flags&~15) | s->outfmt;
}


void SL_HalveSample(SAMPLOAD *s)
{
    if(s->scalefactor)
        s->scalefactor++;
    else
        s->scalefactor = 2;

    s->sample->divfactor = s->scalefactor;
    s->sample->length    = s->length / s->scalefactor;
    s->sample->loopstart = s->loopstart / s->scalefactor;
    s->sample->loopend   = s->loopend / s->scalefactor;
}

void SL_FreeSampleList(void)
{
	FreeSampleList();
}
