view minijss/jssmod.c @ 2208:90ec1ec89c56

Revamp the palette handling in lib64gfx somewhat, add helper functions to lib64util for handling external palette file options and add support for specifying one of the "internal" palettes or external (.act) palette file to gfxconv and 64vw.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 14 Jun 2019 05:01:12 +0300
parents 2f8506171064
children 40ccc09f09be
line wrap: on
line source

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


#ifndef JSS_LIGHT

/* Take given data until maxlen reached, make a string.
 * Basically a bit like strndup(), except end marker byte
 * can be specified.
 */
char *jssASCIItoStr(const char * src, const char endByte, const size_t len)
{
    size_t i, k;
    char *res;

    for (i = 0; src[i] != endByte && i < len; )
        i++;

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

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

    res[k] = 0;

    return res;
}


/* Encodes a given 8-bit sample
 */
int jssEncodeSample8(Uint8 * data, const size_t len, const int ops)
{
    Sint8 value = 0;

    for (size_t offs = 0; offs < len; offs++)
    {
        Sint8 t = data[offs];

        if (ops & jsampFlipSign)
            t ^= 0x80;

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

        data[offs] = t;
    }

    return DMERR_OK;
}


/* Decodes a given 16-bit sample
 */
int 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
        Uint8 *bp1, *bp2;
        size_t bufSize = len * sizeof(Sint16);
        Sint16 *tmpBuf;

        if ((tmpBuf = dmMalloc(bufSize)) == NULL)
            return DMERR_MALLOC;

        bp1 = (Uint8 *) data;
        bp2 = bp1 + len;

        for (size_t offs = 0; offs < len; offs++)
        {
            const Sint16 t = tmpBuf[offs];
            bp1[offs] = t >> 8;
            bp2[offs] = t & 0xff;
        }

        memcpy(data, tmpBuf, bufSize);
        dmFree(tmpBuf);

        return jssEncodeSample8((Uint8 *) data, bufSize, ops);
    }
    else
    {
        Sint16 value = 0, *sdata = (Sint16 *) data;

        for (size_t offs = 0; offs < len; offs++)
        {
            Sint16 t;

            if (ops & jsampSwapEndianess)
            {
                const Sint16 p = sdata[offs];
                t = ((p >> 8) & 0xff) | ((p & 0xff) << 8);
            }
            else
                t = sdata[offs];

            if (ops & jsampDelta)
            {
                const int n = t - value;
                value = t;
                t = n;
            }

            if (ops & jsampFlipSign)
                t ^= 0x8000;

            sdata[offs] = t;
        }
    }

    return DMERR_OK;
}

#endif


/* Decodes a given 8-bit sample
 */
int jssDecodeSample8(Uint8 * data, const size_t len, const int ops)
{
    Sint8 value = 0;

    for (size_t offs = 0; offs < len; offs++)
    {
        Sint8 t = data[offs];

        if (ops & jsampDelta)
            t = value = t + value;

        if (ops & jsampFlipSign)
            t ^= 0x80;

        data[offs] = t;
    }

    return DMERR_OK;
}


/* Decodes a given 16-bit sample
 */
int jssDecodeSample16(Uint16 * data, const size_t len, const int ops)
{
    if (ops & jsampSplit)
    {
        size_t bufSize = len * sizeof(Uint16);
        Uint8 *bp1, *bp2;
        Sint16 *tmpBuf;
        int ret;

        if ((ret = jssDecodeSample8((Uint8 *) data, bufSize, ops)) != DMERR_OK)
            return ret;

        if ((tmpBuf = dmMalloc(bufSize)) == NULL)
            return DMERR_MALLOC;

        // Copy original data to temporary buffer
        memcpy(tmpBuf, data, bufSize);

        bp1 = (Uint8 *) tmpBuf;
        bp2 = bp1 + len;

        for (size_t offs = 0; offs < len; offs++)
        {
            data[offs] = (bp1[offs] << 8) | (bp2[offs] & 0xff);
        }

        dmFree(tmpBuf);
    }
    else
    {
        Sint16 value = 0, *sdata = (Sint16 *) data;

        for (size_t offs = 0; offs < len; offs++)
        {
            Sint16 t;

            if (ops & jsampSwapEndianess)
            {
                const Sint16 p = sdata[offs];
                t = ((p >> 8) & 0xff) | ((p & 0xff) << 8);
            }
            else
                t = sdata[offs];

            if (ops & jsampDelta)
                t = value = t + value;

            if (ops & jsampFlipSign)
                t ^= 0x8000;

            sdata[offs] = t;
        }
    }

    return DMERR_OK;
}


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

    if ((*dst = out = dmMalloc(sizeof(Sint16) * len)) == NULL)
        return DMERR_MALLOC;

    for (size_t offs = 0; offs < len; offs++)
    {
        out[offs] = (in[offs] * 256) - 32768;
    }

    return DMERR_OK;
}


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

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

            if (inst->flags & jsf16bit)
                continue;

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

            inst->flags |= jsf16bit;
            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)
{
    JSSModule *module;

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

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

    for (int 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)
{
    if (module == NULL)
        return DMERR_NULLPTR;

    // Free strings
#ifndef JSS_LIGHT
    dmFree(module->moduleName);
    dmFree(module->trackerName);
#endif

    // Free patterns
    for (int i = 0; i < module->npatterns; i++)
        jssFreePattern(module->patterns[i]);

    // Free the "empty" pattern
    jssFreePattern(module->patterns[jsetMaxPatterns]);

    // Free instruments
    for (int i = 0; i < module->ninstruments; i++)
        jssFreeInstrument(module->instruments[i]);

    // Free extended instruments
    for (int i = 0; i < module->nextInstruments; i++)
        jssFreeExtInstrument(module->extInstruments[i]);

    // Free mutex
#ifdef JSS_SUP_THREADS
    dmDestroyMutex(module->mutex);
#endif

    // Free the module structure
    dmMemset(module, 0, sizeof(JSSModule));
    dmFree(module);

    return DMERR_OK;
}


/* Allocates and initializes a internal pattern structure.
 */
JSSPattern *jssAllocatePattern(const int nrows, const int nchannels)
{
    JSSPattern *pattern;
    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
    if ((pattern = dmMalloc0(sizeof(JSSPattern))) == NULL)
        JSSERROR(DMERR_MALLOC, NULL, "Could not allocate pattern structure.\n");

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

    // Initialize structure
    pattern->nrows     = nrows;
    pattern->nchannels = nchannels;

    pnote = pattern->data;
    for (int row = 0; row < nrows; row++)
    for (int chn = 0; chn < nchannels; chn++)
    {
        pnote->note = pnote->instrument = pnote->volume =
            pnote->effect = pnote->param = jsetNotSet;

        pnote++;
    }

    return pattern;
}


void jssFreePattern(JSSPattern *pattern)
{
    if (pattern != NULL)
    {
        dmFree(pattern->data);
        dmFree(pattern);
    }
}


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

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

    return inst;
}


void jssFreeInstrument(JSSInstrument *inst)
{
    if (inst != NULL)
    {
#ifndef JSS_LIGHT
        dmFree(inst->desc);
#endif
        dmFree(inst->data);
        dmFree(inst);
    }
}


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

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

    // Initialize the requisite fields
    for (int i = 0; i < jsetNNotes; i++)
        inst->sNumForNotes[i] = jsetNotSet;

    return inst;
}


void jssFreeExtInstrument(JSSExtInstrument *inst)
{
    if (inst != NULL)
    {
#ifndef JSS_LIGHT
        dmFree(inst->desc);
#endif
        dmFree(inst->instConvTable);
        dmFree(inst);
    }
}