File: DPSLIB.C | Size: 8,012 bytes | Download file | Back to directory listing | BWPOW's homepage
/*  DPSlib, a .DPS sample format extension for Allegro.
 *  Original AUD library Copyright (C) 1998 Peter Wang.
 *  Slightly changed (for DPS) by Samuel Kupka
 * 
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 * 
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 * 
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the
 *  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *  Boston, MA  02111-1307, USA.
 *
 *  Contacting the author of original library (and Psyk Software):
 *      Email: tjaden@psynet.net
 *      WWW: http://www.psynet.net/tjaden
 *
 *  Contacting Samuel Kupka (and IdeaZ software)
 *      Email: bwpow@ideaz.sk
 *      WWW: http://www.ideaz.sk
 *
 *  See readme.txt for more information.
 */
 
#include <stdio.h>
#include <string.h>
#include <allegro.h>
#include "dpslib.h"
 
#define BUFFER_SIZE 8192    /* MUST be a multiple of 2048! */
 
/*
 *
 *  Internal use only
 *
 */
 
/* IMA-ADPCM compression / decompression tables */
static int index_table[16] = { 
    -1, -1, -1, -1, 2, 4, 6, 8,
    -1, -1, -1, -1, 2, 4, 6, 8
};
 
static int step_table[89] = 
{
    7,     8,     9,     10,    11,    12,     13,    14,    16,
    17,    19,    21,    23,    25,    28,     31,    34,    37,
    41,    45,    50,    55,    60,    66,     73,    80,    88,
    97,    107,   118,   130,   143,   157,    173,   190,   209,
    230,   253,   279,   307,   337,   371,    408,   449,   494,
    544,   598,   658,   724,   796,   876,    963,   1060,  1166,
    1282,  1411,  1552,  1707,  1878,  2066,   2272,  2499,  2749,
    3024,  3327,  3660,  4026,  4428,  4871,   5358,  5894,  6484,
    7132,  7845,  8630,  9493,  10442, 11487,  12635, 13899, 15289,
    16818, 18500, 20350, 22385, 24623, 27086,  29794, 32767 
};
 
/* the current .dps */
static char *fn;
static FILE *fp;
static AUDIOSTREAM *stream;
static int looping;
 
/* the current chunk's data */
static unsigned char *data = NULL;
static int data_size = 0;
static int data_index, low_nibble;
int dps_chunks,now_chunk=0,schunk=0;
 
/* decompression variables */
static int step_index[2], valpred[2];
 
/* error strings */
static char *no_file        = "Error opening file";
static char *not_dps        = "Not valid DPS file";
static char *bad_stream     = "Error creating audio stream";
 
/* FIXME: I hate this.  Prevents the `cutoff' at the end of the buffer. */
static ugly_hack = 0; 
 
 
/*
 *
 *  Exported globals
 *
 */
 
int dps_loaded = FALSE;
int dps_paused = FALSE;
int dps_freq, dps_stereo;
int dps_volume = 255;
char *dps_error = NULL;
 
 
/*
 *
 *  Functions
 *
 */
 
void unload_dps_stream_file(void)
{
    ugly_hack = 0;
 
    if (dps_loaded)
    {
	dps_loaded = FALSE;
	fclose(fp);
    }
}
 
void unload_dps_stream(void)
{
    ugly_hack = 0;
 
    if (dps_loaded)
    {
	free(data);
	data = NULL;
	data_size = 0;
	free(fn);
        unload_dps_stream_file();
	stop_audio_stream(stream);
    }
}
 
int load_dps_stream_file(char *filename,int loop)
{
    unsigned short  freq;       /* frequency */
    unsigned char   flags;      /* 1 - mono, 2 - stereo */
    unsigned char   ver;        /* DPS version (0) */
    unsigned char   head[5];    /* Head string */
 
    fp = fopen(filename, "rb");
    if (!fp)
    {
	dps_error = no_file;
	return FALSE;
    }
 
    dps_error = NULL;
 
    fread(head,1,4,fp);
    fread(&ver,1,1,fp); // version (0)
 
    fread(&flags,1,1,fp);
    fread(&freq,sizeof(short),1,fp);
    fread(&dps_chunks,sizeof(short),1,fp);
 
    dps_freq = freq;
    dps_stereo = flags-1;//(flags & 1) ? TRUE : FALSE;
 
    now_chunk=0;
 
    step_index[0] = valpred[0] = 0;
    step_index[1] = valpred[1] = 0;
 
    dps_loaded = TRUE; 
    dps_paused = FALSE;
 
    return TRUE;
}
 
int load_dps_stream(char *filename, int loop)
{
    unload_dps_stream();
 
    if(load_dps_stream_file(filename,loop)==FALSE) return FALSE;
 
    stream = play_audio_stream(BUFFER_SIZE, 16, dps_stereo, dps_freq, dps_volume, 128);
    if (!stream)
    {
	dps_error = bad_stream;
	fclose(fp);
	return FALSE;
    }
 
    fn = strdup(filename);
    looping = loop;
 
    data=malloc(1024);
    data_size=1024;
 
    return TRUE;
}
 
void set_dps_volume(int vol)
{
    if (vol>255) vol=255;
    else if (vol<0) vol=0;
    dps_volume = vol;
 
    if (dps_loaded)
	voice_set_volume(stream->voice, vol);
}
 
int get_next_chunk()
{
    unsigned short  size;       /* size of compressed data */
 
    if(now_chunk>=dps_chunks) return 0;
    now_chunk++;
 
    if (feof(fp))
	return 0;
 
    fread(&schunk,1,1,fp);
    fread(&size,sizeof(short),1,fp);
 
    if (data_size < size) {
	data_size = size;
	if (data) data = realloc(data, size);
	else data = malloc(size);
    }
 
    fread(data,1,size, fp);
    data_index = 0;
    low_nibble = TRUE;
    return size;
}
 
int get_next_delta()
{
    if (low_nibble == TRUE)
    {
	low_nibble = FALSE;
	return data[data_index] & 0xf;
    }
    else
    {
	low_nibble = TRUE;
	return (data[data_index++]>>4) & 0xf;
    }
}
 
void restart_dps(void)
{
    if (dps_loaded)
    {
	/* cop out, but anyway */
	unload_dps_stream_file();
	load_dps_stream_file(fn, looping);
    }
}
 
int poll_dps(void)
{
    unsigned short *o;
    int step[2],  delta, vpdiff;
    int count, i, ch=0;
    static int ugly_hack_last_value = 0;
    char dps_end=0;
 
 
    if (!dps_loaded) 
	return -1;
 
 
    o = get_audio_stream_buffer(stream);
    if (o)
    {
	i = BUFFER_SIZE << (dps_stereo?1:0);
 
	/* Paused, so play silence. */
	if (dps_paused)
	{
	    while (i--)
		*o++ = ugly_hack_last_value;
 
	    free_audio_stream_buffer(stream);
	    return 1; 
	}
 
	/* Reached end of dps. */
	if (feof(fp)) {
	    if (!ugly_hack) { 
		ugly_hack = 1;
 
		while (i--)
		    *o++ = ugly_hack_last_value;
 
		free_audio_stream_buffer(stream);
		return 1; 
	    } else {
		ugly_hack = 0;
 
                dps_end=1;
                goto end;
	    }
	}
 
	/* Read data from file. */
	while (i)
	{
	    count = get_next_chunk() << 1;
            ch=0;
	    if (count == 0)
	    {
		while (--i)
		    *o++ = ugly_hack_last_value;
                dps_end=1;
	    }
	    else
	    {
		while (count-- > 0)
		{
		    delta = get_next_delta();
 
		    /* update step value */
		    step[ch] = step_table[step_index[ch]];
 
		    /* compute difference and new predicted value */
		    vpdiff = step[ch] >> 3;
		    if (delta & 4) vpdiff += step[ch];
		    if (delta & 2) vpdiff += step[ch] >> 1;
		    if (delta & 1) vpdiff += step[ch] >> 2;
 
		    /* calculate new sample value */
		    if (delta & 0x8) valpred[ch] -= vpdiff;
		    else valpred[ch] += vpdiff;
 
		    /* clamp */
		    if (valpred[ch] > 32767) valpred[ch] = 32767;
		    else if (valpred[ch] < -32768) valpred[ch] = -32768;
 
		    /* find new index value */
		    step_index[ch] += index_table[delta];
		    if (step_index[ch] < 0) step_index[ch] = 0;
		    else if (step_index[ch] > 88) step_index[ch] = 88;
 
		    /* output */
		    *o++ = valpred[ch]^0x8000;
 
                    if(schunk){
  		      *o++ = valpred[ch]^0x8000;
                      i--;
                    }
 
		    i--;
 
		    /* separate channel decoding */
		    if (dps_stereo&&schunk==0) {
			if (ch==0) ch=1;
			else ch=0;
		    }
		}
 
		ugly_hack_last_value = valpred[ch]^0x8000;
	    }
	}
 
	free_audio_stream_buffer(stream);
 
        end:;
        if(dps_end){
 
          if(looping){
            restart_dps();
          }
          else
            unload_dps_stream();
        }
 
	return 1;
    }
    else 
	return 0;
}
 
void pause_dps(void)
{
    dps_paused = TRUE;
}
 
void resume_dps(void)
{
    dps_paused = FALSE;
}