/* 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; }