view jssmod.c @ 49:033c660c25f5

Restructure module playing, removing 8bit sample mixing (output can still be 8bit, but samples are internally upconverted to 16bit after module loading.) Also prepare for floating point mixing support.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 01 Oct 2012 02:51:41 +0300
parents 064d1d1d5b0f
children 36e2f910219c
line wrap: on
line source

/*
 * miniJSS - Module structure and handling routines
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2006-2007 Tecnic Software productions (TNSP)
 */
#include "jssmod.h"
#include <string.h>


#ifndef JSS_LIGHT
/* Take given data until maxlen reached, make a string
 */
char *jssASCIItoStr(char * sdata, const char endByte, const size_t maxLen)
{
    size_t i, k;
    char *res;

    for (i = 0; sdata[i] && i < maxLen; i++);

    res = (char *) dmMalloc(i + 1);
    if (res == NULL)
        return NULL;

    for (k = 0; sdata[k] != endByte && k < i; k++)
        res[k] = sdata[k];

    res[k] = 0;

    return res;
}


/* Encodes a given 8-bit sample
 */
BOOL jssEncodeSample8(Uint8 * data, const size_t len, const int ops)
{
    size_t count = len;
    Sint8 t, value = 0;
    
    while (count--)
    {
        t = *data;
        
        if (ops & jsampFlipSign)
            t ^= 0x80;

        if (ops & jsampDelta)
        {
            int n = t - value;
            value = t;
            t = n;
        }
        
        *(data++) = t;
    }

    return TRUE;
}


/* Decodes a given 16-bit sample
 */
BOOL jssEncodeSample16(Uint16 * data, const size_t len, const int ops)
{
    // "Split" the 16-bit samples into 8-bit halves
    if (ops & jsampSplit)
    {
        // Allocate temporary processing buffer
        size_t count, bufSize = len * sizeof(Sint16);
        Uint8 *bp1, *bp2;
        Sint16 *sdata, *tmpBuf = dmMalloc(bufSize);
        if (tmpBuf == NULL) return FALSE;

        sdata = tmpBuf;
        bp1 = (Uint8 *) data;
        bp2 = bp1 + len;
        count = len;

        while (count--)
        {
            Sint16 t = (*sdata++);
            *bp1++ = t >> 8;
            *bp2++ = t & 0xff;
        }
        
        memcpy(data, tmpBuf, bufSize);
        dmFree(tmpBuf);
        
        return jssEncodeSample8((Uint8 *) data, bufSize, ops);
    }
    else
    {
        Sint16 t, p, value = 0, *sdata = (Sint16 *) data;
        size_t count = len;
        
        while (count--)
        {
            if (ops & jsampSwapEndianess)
            {
                p = *sdata;
                t = ((p >> 8) & 0xff) | ((p & 0xff) << 8);
            } else
                t = *sdata;
            
            if (ops & jsampDelta)
            {
                int n = t - value;
                value = t;
                t = n;
            }
            
            if (ops & jsampFlipSign)
                t ^= 0x8000;
            
            *(sdata++) = t;
        }
    }
    return TRUE;
}

#endif


/* Decodes a given 8-bit sample
 */
BOOL jssDecodeSample8(Uint8 * data, const size_t len, const int ops)
{
    size_t count = len;
    Sint8 t, value = 0;
    
    while (count--)
    {
        t = *data;
        
        if (ops & jsampDelta)
            t = value = t + value;
        
        if (ops & jsampFlipSign)
            t ^= 0x80;
        
        *(data++) = t;
    }
    return TRUE;
}


/* Decodes a given 16-bit sample
 */
BOOL jssDecodeSample16(Uint16 * data, const size_t len, const int ops)
{
    if (ops & jsampSplit)
    {
        size_t count, bufSize = len * sizeof(Uint16);
        Uint8 *bp1, *bp2;
        Sint16 *tmpBuf, *sdata;
        
        if (!jssDecodeSample8((Uint8 *) data, bufSize, ops))
            return FALSE;
        
        tmpBuf = dmMalloc(bufSize);
        if (tmpBuf == NULL) return FALSE;
        memcpy(tmpBuf, data, bufSize);
        
        sdata = (Sint16 *) data;
        bp1 = (Uint8 *) tmpBuf;
        bp2 = bp1 + len;
        count = len;
        while (count--)
        {
            *sdata++ = (*bp1++ << 8) | (*bp2++ & 0xff);
        }
        
        dmFree(tmpBuf);
    }
    else
    {
        Sint16 t, p, value = 0, *sdata = (Sint16 *) data;
        size_t count = len;
        while (count--)
        {
            if (ops & jsampSwapEndianess)
            {
                p = *sdata;
                t = ((p >> 8) & 0xff) | ((p & 0xff) << 8);
            } else
                t = *sdata;
            
            if (ops & jsampDelta)
                t = value = t + value;
            
            if (ops & jsampFlipSign)
                t ^= 0x8000;
            
            *(sdata++) = t;
        }
    }
    return TRUE;
}

#ifdef JSS_MIX_FP
/* Convert sample data from S16 or U8 to floating point
 */
int jssConvertSampleToFP(void **dst, void * src, const size_t len, const int flags)
{
    size_t count = len;
    float *out;

    *dst = out = dmMalloc(sizeof(float) * len);
    if (out == NULL)
        return DMERR_MALLOC;
    
    if (flags & jsf16bit)
    {
        Sint16 *in = (Sint16 *) src;
        while (count--)
        {
            *(out++) = (float) (*(in++)) / 32768.0f;
        }
    }
    else
    {
        Uint8 *in = (Uint8 *) src;
        while (count--)
        {
            *(out++) = (float) (*(in++) - 128) / 128.0f;
        }
    }
    
    return DMERR_OK;
}

#endif

/* Convert sample data from U8 to S16
 */
int jssConvertSampleTo16(void **dst, void * src, const size_t len)
{
    size_t count = len;
    Uint8 *in = (Uint8 *) src;
    Sint16 *out;

    *dst = out = dmMalloc(sizeof(Sint16) * len);
    if (out == NULL)
        return DMERR_MALLOC;
    
    while (count--)
    {
        *(out++) = (*(in++) * 256) - 32768;
    }
    
    return DMERR_OK;
}

/* Converts the given module in preparation for playing it.
 * This involves sample format conversion (8 to 16 bit, or
 * if floating point mixing is enabled, 8/16 bit to FP.)
 *
 * NOTICE! The converted module can only be saved in JSSMOD
 * format, but this is not recommended.
 */
int jssConvertModuleForPlaying(JSSModule *module)
{
    int i;
    if (module == NULL)
        return DMERR_NULLPTR;

    // Convert instruments
    for (i = 0; i < module->ninstruments; i++)
    {
        JSSInstrument *inst = module->instruments[i];
        if (inst != NULL && inst->data != NULL)
        {
            int res;
            void *data = NULL;
#ifdef JSS_MIX_FP
            if (inst->flags & jsfFP)
                continue;

            if ((res = jssConvertSampleToFP(&data, inst->data, inst->size, inst->flags)) != DMERR_OK)
                return res;
            
            inst->flags &= !(jsf16bit);
            inst->flags |= jfsFP;
#else
            if (inst->flags & jsf16bit)
                continue;

            if ((res = jssConvertSampleTo16(&data, inst->data, inst->size)) != DMERR_OK)
                return res;

            inst->flags |= jsf16bit;
#endif
            dmFree(inst->data);
            inst->data = data;
        }
    }
    
    return DMERR_OK;
}


/* Allocates a new module structure or returns errorvalue if failed.
 * Memory is allocated only for the basic structure. Sample- and pattern
 * areas must be allocated separately with appropriate routines.
 */
JSSModule *jssAllocateModule(void)
{
    int i;
    JSSModule *module;

    // Allocate module structure
    module = dmMalloc0(sizeof(JSSModule));
    if (module == NULL)
        return NULL;

    // Initialize structure
    for (i = 0; i < jsetNChannels; i++)
        module->defPanning[i] = jchPanMiddle;

    for (i = 0; i < jsetMaxOrders; i++)
        module->orderList[i] = jsetOrderEnd;

    // Allocate mutex
#ifdef JSS_SUP_THREADS
    module->mutex = dmCreateMutex();
#endif

    return module;
}


/* Frees a given module structure, freeing all memory areas
 * that were allocated for it (including patterns, samples, etc.)
 */
int jssFreeModule(JSSModule * module)
{
    int i;

    if (module == NULL)
        return DMERR_NULLPTR;
    
    // Free strings
#ifndef JSS_LIGHT
    dmFree(module->moduleName);
    dmFree(module->trackerName);
#endif

    // Free patterns
    for (i = 0; i < module->npatterns; i++)
    {
        if (module->patterns[i])
        {
            JSSPattern *pat = module->patterns[i];
            dmFree(pat->data);
            dmFree(pat);
            module->patterns[i] = NULL;
        }
    }

    // Free instruments
    for (i = 0; i < module->ninstruments; i++)
    {
        if (module->instruments[i])
        {
            JSSInstrument *inst = module->instruments[i];
#ifndef JSS_LIGHT
            dmFree(inst->desc);
#endif
            dmFree(inst->data);
            dmFree(inst);
            module->instruments[i] = NULL;
        }
    }

    // Free extended instruments
    for (i = 0; i < module->nextInstruments; i++)
    {
        if (module->extInstruments[i])
        {
            JSSExtInstrument *ext = module->extInstruments[i];
#ifndef JSS_LIGHT
            dmFree(ext->desc);
#endif
            dmFree(ext);
            module->extInstruments[i] = NULL;
        }
    }

    // Free mutex
#ifdef JSS_SUP_THREADS
    dmDestroyMutex(module->mutex);
#endif
    
    // Free the module structure
    memset(module, 0, sizeof(JSSModule));
    dmFree(module);

    return DMERR_OK;
}


/* Allocates and initializes a internal pattern structure.
 */
JSSPattern *jssAllocatePattern(int nrows, int nchannels)
{
    int i, j;
    JSSPattern *res;
    JSSNote *pnote;

    // Check arguments
    if (nrows <= 0 || nchannels <= 0)
        JSSERROR(DMERR_INVALID_ARGS, NULL, "Invalid nrows=%i or nchannels=%i.\n", nrows, nchannels);

    // Allocate a pattern structure
    res = dmMalloc0(sizeof(JSSPattern));
    if (res == NULL)
        JSSERROR(DMERR_MALLOC, NULL, "Could not allocate pattern structure.\n");

    // Allocate notedata
    res->data = dmCalloc(nrows * nchannels, sizeof(JSSNote));
    if (res->data == NULL)
    {
        dmFree(res);
        JSSERROR(DMERR_MALLOC, NULL, "Could not allocate pattern data (nrows=%i, nchannels=%i).\n", nrows,
             nchannels);
    }

    // Initialize
    res->nrows = nrows;
    res->nchannels = nchannels;

    pnote = res->data;
    for (j = 0; j < nrows; j++)
    {
        for (i = 0; i < nchannels; i++)
        {
            pnote->note = pnote->instrument = pnote->volume =
                pnote->effect = pnote->param = jsetNotSet;

            pnote++;
        }
    }

    // OK, return pointer to struct
    return res;
}


/* Allocates and initializes internal "normal" instrument structure.
 */
JSSInstrument *jssAllocateInstrument(void)
{
    JSSInstrument *res;

    // Allocate a instrument structure
    res = dmMalloc0(sizeof(JSSInstrument));
    if (res == NULL)
        return NULL;

    return res;
}


/* Allocates and initializes "extended" instrument structure.
 */
JSSExtInstrument *jssAllocateExtInstrument(void)
{
    int i;
    JSSExtInstrument *res;

    // Allocate a instrument structure
    res = dmMalloc0(sizeof(JSSExtInstrument));
    if (res == NULL)
        return NULL;

    for (i = 0; i < jsetNNotes; i++)
    {
        res->sNumForNotes[i] = jsetNotSet;
    }
    
    return res;
}