musix 0.3.3

Music player library for esoteric audio formats (music from C64,Amiga etc)
Documentation
#include <stdlib.h>
#include <string.h>

#include "../usf/usf.h"

#include <psflib.h>

unsigned char * state = 0;

unsigned int enable_compare = 0;
unsigned int enable_fifo_full = 0;

unsigned long length_ms = 0;
unsigned long fade_ms = 0;

static void * stdio_fopen( const char * path )
{
    return fopen( path, "rb" );
}

static size_t stdio_fread( void *p, size_t size, size_t count, void *f )
{
    return fread( p, size, count, (FILE*) f );
}

static int stdio_fseek( void * f, int64_t offset, int whence )
{
    return fseek( (FILE*) f, offset, whence );
}

static int stdio_fclose( void * f )
{
    return fclose( (FILE*) f );
}

static long stdio_ftell( void * f )
{
    return ftell( (FILE*) f );
}

static psf_file_callbacks stdio_callbacks =
{
    "\\/:",
    stdio_fopen,
    stdio_fread,
    stdio_fseek,
    stdio_fclose,
    stdio_ftell
};

static int usf_loader(void * context, const uint8_t * exe, size_t exe_size,
                      const uint8_t * reserved, size_t reserved_size)
{
    if ( exe && exe_size > 0 ) return -1;

    return usf_upload_section( state, reserved, reserved_size );
}

#define BORK_TIME 0xC0CAC01A

static unsigned long parse_time_crap(const char *input)
{
    unsigned long value = 0;
    unsigned long multiplier = 1000;
    const char * ptr = input;
    unsigned long colon_count = 0;
    
    while (*ptr && ((*ptr >= '0' && *ptr <= '9') || *ptr == ':'))
    {
        colon_count += *ptr == ':';
        ++ptr;
    }
    if (colon_count > 2) return BORK_TIME;
    if (*ptr && *ptr != '.' && *ptr != ',') return BORK_TIME;
    if (*ptr) ++ptr;
    while (*ptr && *ptr >= '0' && *ptr <= '9') ++ptr;
    if (*ptr) return BORK_TIME;
    
    ptr = strrchr(input, ':');
    if (!ptr)
        ptr = input;
    for (;;)
    {
        char * end;
        if (ptr != input) ++ptr;
        if (multiplier == 1000)
        {
            double temp = strtod(ptr, &end);
            if (temp >= 60.0) return BORK_TIME;
            value = (long)(temp * 1000.0f);
        }
        else
        {
            unsigned long temp = strtoul(ptr, &end, 10);
            if (temp >= 60 && multiplier < 3600000) return BORK_TIME;
            value += temp * multiplier;
        }
        if (ptr == input) break;
        ptr -= 2;
        while (ptr > input && *ptr != ':') --ptr;
        multiplier *= 60;
    }
    
    return value;
}

static int usf_info(void * context, const char * name, const char * value)
{
    if (!strcasecmp(name, "length"))
        length_ms += parse_time_crap(value);
    else if (!strcasecmp(name, "fade"))
        fade_ms += parse_time_crap(value);
    else if (!strcasecmp(name, "_enablecompare") && *value)
        enable_compare = 1;
    else if (!strcasecmp(name, "_enablefifofull") && *value)
        enable_fifo_full = 1;
    
    return 0;
}

int main(int argc, char ** argv)
{
	if ( argc == 3 )
	{
        FILE * f;
        
        int32_t sample_rate;
        int32_t length_to_render;
        int32_t fade_total;
        int32_t i;
        
        int16_t sample_buffer[2048];
        
        state = (unsigned char *) malloc(usf_get_state_size());
        
        usf_clear(state);
 
		if ( psf_load( argv[1], &stdio_callbacks, 0x21, usf_loader, 0, usf_info, 0, 1 ) <= 0 )
            return 1;
        
        usf_set_compare(state, enable_compare);
        usf_set_fifo_full(state, enable_fifo_full);
        
        usf_set_hle_audio(state, 1);
        
        f = fopen(argv[2], "wb");
        
        length_to_render = (int32_t)(length_ms * 441 / 10);
        
        while (length_to_render)
        {
            int32_t samples_todo = length_to_render;
            if (samples_todo > 1024)
                samples_todo = 1024;
            
            usf_render_resampled(state, sample_buffer, samples_todo, 44100);
            fwrite(sample_buffer, samples_todo, 4, f);
            
            length_to_render -= samples_todo;
        }
        
        fade_total = length_to_render = (int32_t)(fade_ms * 441 / 10);
        
        while (length_to_render)
        {
            int32_t samples_todo = length_to_render;
            if (samples_todo > 1024)
                samples_todo = 1024;
            
            usf_render_resampled(state, sample_buffer, samples_todo, 44100);
            
            for (i = 0; i < samples_todo; ++i)
            {
                sample_buffer[i * 2 + 0] = (int64_t)sample_buffer[i * 2 + 0] * (length_to_render - i) / fade_total;
                sample_buffer[i * 2 + 1] = (int64_t)sample_buffer[i * 2 + 1] * (length_to_render - i) / fade_total;
            }
            
            fwrite(sample_buffer, samples_todo, 4, f);
            
            length_to_render -= samples_todo;
        }
        
        fclose(f);
        
        usf_shutdown(state);
        
        free(state);
	}
    
    return 0;
}