diff --git a/sc2/src/sc2code/libs/sound/decoders/Makeinfo b/sc2/src/sc2code/libs/sound/decoders/Makeinfo index ca15a69eb..dad555470 100644 --- a/sc2/src/sc2code/libs/sound/decoders/Makeinfo +++ b/sc2/src/sc2code/libs/sound/decoders/Makeinfo @@ -1,2 +1,2 @@ uqm_SUBDIRS="mikmod" -uqm_CFILES="decoder.c wav.c" +uqm_CFILES="decoder.c wav.c dukaud.c" diff --git a/sc2/src/sc2code/libs/sound/decoders/decoder.c b/sc2/src/sc2code/libs/sound/decoders/decoder.c index ebb78ead6..560d26f4c 100644 --- a/sc2/src/sc2code/libs/sound/decoders/decoder.c +++ b/sc2/src/sc2code/libs/sound/decoders/decoder.c @@ -34,6 +34,7 @@ #include "wav.h" #include "libs/sound/sound_common.h" #include "mikmod/mikmod.h" +#include "dukaud.h" extern bool fileExists2(uio_DirHandle *dir, const char *fileName); // Can't '#include "libs/file.h"' due to some type conflicts. @@ -44,6 +45,7 @@ static char *decoder_info_wav = "Wav"; static char *decoder_info_mod = "MikMod"; static char *decoder_info_ogg = "Ogg Vorbis"; static char *decoder_info_nul = "None"; +static char *decoder_info_duk = "DukAud"; TFB_DecoderFormats decoder_formats; @@ -231,7 +233,7 @@ TFB_SoundDecoder* SoundDecoder_Load (uio_DirHandle *dir, char *filename, mod->panflag = 1; mod->wrap = 0; mod->loop = 1; - + decoder = (TFB_SoundDecoder *) HMalloc (sizeof (TFB_SoundDecoder)); decoder->buffer = HMalloc (buffer_size); decoder->buffer_size = buffer_size; @@ -333,6 +335,43 @@ TFB_SoundDecoder* SoundDecoder_Load (uio_DirHandle *dir, char *filename, return decoder; } + else if (!strcmp (&filename[i], "duk")) + { + TFB_SoundDecoder *decoder; + DukAud_Track* track; + + track = duka_openTrack (dir, filename); + if (!track) + { + fprintf (stderr, "SoundDecoder_Load(): couldn't load %s\n", filename); + return NULL; + } + + decoder = (TFB_SoundDecoder *) HMalloc (sizeof (TFB_SoundDecoder)); + decoder->buffer = HMalloc (buffer_size); + decoder->buffer_size = buffer_size; + decoder->frequency = track->rate; + decoder->looping = false; + decoder->error = SOUNDDECODER_OK; + decoder->length = track->length - (startTime / 1000.0f); + decoder->start_sample = 0; + decoder->end_sample = 0; + decoder->pos = 0; + + if (track->channels == 1) + decoder->format = decoder_formats.mono16; + else + decoder->format = decoder_formats.stereo16; + + decoder->decoder_info = decoder_info_duk; + decoder->type = SOUNDDECODER_DUK; + decoder->dir = dir; + decoder->filename = (char *) HMalloc (strlen (filename) + 1); + strcpy (decoder->filename, filename); + decoder->data = track; + + return decoder; + } else { fprintf (stderr, "SoundDecoder_Load(): %s file format is unsupported\n", filename); @@ -470,6 +509,37 @@ uint32 SoundDecoder_Decode (TFB_SoundDecoder *decoder) //fprintf (stderr, "SoundDecoder_Decode(): decoded %d bytes from %s\n", decoded_bytes, decoder->filename); return decoded_bytes; } + case SOUNDDECODER_DUK: + { + DukAud_Track *track = (DukAud_Track *) decoder->data; + uint32 decoded_bytes = 0; + sint32 rc; + + rc = duka_readData (track, decoder->buffer, decoder->buffer_size); + if (rc < 0) + { + decoder->error = SOUNDDECODER_ERROR; + fprintf (stderr, "SoundDecoder_Decode(): error decoding %s, code %d\n", decoder->filename, rc); + return decoded_bytes; + } + decoded_bytes = rc; + decoder->pos += decoded_bytes; + decoder->error = rc > 0 ? SOUNDDECODER_OK : SOUNDDECODER_EOF; + //fprintf (stderr, "SoundDecoder_Decode(): decoded %d bytes from %s\n", decoded_bytes, decoder->filename); + + /* DukAud produces output in machine byte-order + * may need to do a word-swap + */ + if (decoded_bytes > 0 && + decoder_formats.big_endian != decoder_formats.want_big_endian && + (decoder->format == decoder_formats.stereo16 || + decoder->format == decoder_formats.mono16)) + { + SoundDecoder_SwapWords ( + decoder->buffer, decoded_bytes); + } + return decoded_bytes; + } case SOUNDDECODER_NULL: { uint32 max_bytes; @@ -591,6 +661,11 @@ uint32 SoundDecoder_DecodeAll (TFB_SoundDecoder *decoder) return decoded_bytes; break; } + case SOUNDDECODER_DUK: + { + fprintf (stderr, "SoundDecoder_DecodeAll(): unimplemented for duk\n"); + break; + } case SOUNDDECODER_NULL: { decoder->error = SOUNDDECODER_OK; @@ -656,6 +731,24 @@ void SoundDecoder_Seek (TFB_SoundDecoder *decoder, uint32 seekTime) decoder->error = SOUNDDECODER_OK; return; } + case SOUNDDECODER_DUK: + { + DukAud_Track* track = (DukAud_Track*) decoder->data; + + if (duka_seekTrack (track, (float)seekTime / 1000.0f)) + { + decoder->pos = seekTime * decoder->frequency * 2 / 1000; + if (decoder->format == decoder_formats.stereo16) + decoder->pos *= 2; + } + else + { + fprintf (stderr, "SoundDecoder_Seek(): couldn't seek %s to %u\n", decoder->filename, seekTime); + break; + } + decoder->error = SOUNDDECODER_OK; + return; + } case SOUNDDECODER_BUF: { uint32 pcm_pos = seekTime * decoder->frequency / 1000; @@ -714,6 +807,13 @@ void SoundDecoder_Free (TFB_SoundDecoder *decoder) HFree (vf); break; } + case SOUNDDECODER_DUK: + { + DukAud_Track* track = (DukAud_Track*) decoder->data; + //fprintf (stderr, "SoundDecoder_Free(): freeing %s\n", decoder->filename); + duka_closeTrack (track); + break; + } case SOUNDDECODER_BUF: break; case SOUNDDECODER_NULL: @@ -750,6 +850,7 @@ float SoundDecoder_GetTime (TFB_SoundDecoder *decoder) return 0.0f; } case SOUNDDECODER_OGG: + case SOUNDDECODER_DUK: case SOUNDDECODER_BUF: case SOUNDDECODER_NULL: { @@ -767,3 +868,47 @@ float SoundDecoder_GetTime (TFB_SoundDecoder *decoder) } } } + +uint32 +SoundDecoder_GetFrame (TFB_SoundDecoder *decoder) +{ + uint32 frame = 0; + + if (!decoder) + return 0; + + switch (decoder->type) + { + case SOUNDDECODER_MOD: + { + MODULE *mod = (MODULE *) decoder->data; + frame = mod->sngpos; + break; + } + case SOUNDDECODER_OGG: + { + OggVorbis_File *vf = (OggVorbis_File *) decoder->data; + // this is the closest to a frame there is in ogg vorbis stream + // doesn't seem to be a func to retrive it + frame = vf->os.pageno; + break; + } + case SOUNDDECODER_DUK: + { + DukAud_Track* track = (DukAud_Track*) decoder->data; + frame = duka_getFrame (track); + break; + } + case SOUNDDECODER_WAV: + case SOUNDDECODER_BUF: + case SOUNDDECODER_NULL: + break; + default: + { + fprintf (stderr, "SoundDecoder_GetFrame(): unknown type %d\n", decoder->type); + break; + } + } + + return frame; +} diff --git a/sc2/src/sc2code/libs/sound/decoders/decoder.h b/sc2/src/sc2code/libs/sound/decoders/decoder.h index 0363c1f2c..42885192a 100644 --- a/sc2/src/sc2code/libs/sound/decoders/decoder.h +++ b/sc2/src/sc2code/libs/sound/decoders/decoder.h @@ -79,6 +79,7 @@ enum SOUNDDECODER_OGG, SOUNDDECODER_NULL, SOUNDDECODER_BUF, + SOUNDDECODER_DUK, }; extern TFB_DecoderFormats decoder_formats; @@ -90,6 +91,7 @@ TFB_SoundDecoder* SoundDecoder_Load (uio_DirHandle *dir, uint32 SoundDecoder_Decode (TFB_SoundDecoder *decoder); uint32 SoundDecoder_DecodeAll (TFB_SoundDecoder *decoder); float SoundDecoder_GetTime (TFB_SoundDecoder *decoder); +uint32 SoundDecoder_GetFrame (TFB_SoundDecoder *decoder); void SoundDecoder_Seek (TFB_SoundDecoder *decoder, uint32 pcm_pos); void SoundDecoder_Rewind (TFB_SoundDecoder *decoder); void SoundDecoder_Free (TFB_SoundDecoder *decoder); diff --git a/sc2/src/sc2code/libs/sound/decoders/dukaud.c b/sc2/src/sc2code/libs/sound/decoders/dukaud.c new file mode 100644 index 000000000..f17e4c1a4 --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/dukaud.c @@ -0,0 +1,428 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* .duk sound track decoder + */ + +#include +#include +#include +#include "libs/misc.h" +#include "dukaud.h" +#include "decoder.h" +#include "endian_uqm.h" + +#define DATA_BUF_SIZE 0x8000 +#define DUCK_GENERAL_FPS 14.622f + +static sint32 duka_error = 0; + +typedef struct +{ + uint32 audsize; + uint32 vidsize; +} DukAud_FrameHeader; + +typedef struct +{ + uint16 magic; // always 0xf77f + uint16 numsamples; + uint16 tag; + uint16 indices[2]; // initial indices for channels +} DukAud_AudSubframe; + +sint32 +duka_getError () +{ + sint32 ret = duka_error; + duka_error = dukae_None; + return ret; +} + +sint32 +duka_readAudFrameHeader (DukAud_Track* track, uint32 iframe, + DukAud_AudSubframe* aud) +{ + DukAud_FrameHeader hdr; + + uio_fseek (track->duk, track->frames[iframe], SEEK_SET); + if (uio_fread (&hdr, sizeof(hdr), 1, track->duk) != 1) + { + duka_error = errno; + return dukae_BadFile; + } + hdr.audsize = UQM_SwapBE32 (hdr.audsize); + + if (uio_fread (aud, sizeof(*aud), 1, track->duk) != 1) + { + duka_error = errno; + return dukae_BadFile; + } + + aud->magic = UQM_SwapBE16 (aud->magic); + if (aud->magic != 0xf77f) + return duka_error = dukae_BadFile; + + aud->numsamples = UQM_SwapBE16 (aud->numsamples); + aud->tag = UQM_SwapBE16 (aud->tag); + aud->indices[0] = UQM_SwapBE16 (aud->indices[0]); + aud->indices[1] = UQM_SwapBE16 (aud->indices[1]); + + return 0; +} + +DukAud_Track* +duka_openTrack (uio_DirHandle *dir, const char *file) +{ + DukAud_Track* track; + uio_Stream* duk; + uio_Stream* frm; + DukAud_AudSubframe aud; + char filename[256]; + uint32 filelen; + size_t cread; + uint32 i; + + filelen = strlen (file); + if (filelen > sizeof (filename) - 1) + return NULL; + strcpy (filename, file); + + duk = uio_fopen (dir, filename, "rb"); + if (!duk) + { + duka_error = errno; + return NULL; + } + + strcpy (filename + filelen - 3, "frm"); + frm = uio_fopen (dir, filename, "rb"); + if (!frm) + { + duka_error = errno; + uio_fclose (duk); + return NULL; + } + + track = (DukAud_Track *) HCalloc (sizeof (DukAud_Track)); + track->duk = duk; + + uio_fseek (frm, 0, SEEK_END); + track->cframes = uio_ftell (frm) / sizeof (uint32); + uio_fseek (frm, 0, SEEK_SET); + if (!track->cframes) + { + duka_error = dukae_BadFile; + uio_fclose (frm); + duka_closeTrack (track); + return NULL; + } + + track->frames = (uint32*) HMalloc (track->cframes * sizeof (uint32)); + cread = uio_fread (track->frames, sizeof (uint32), track->cframes, frm); + uio_fclose (frm); + if (cread != track->cframes) + { + duka_error = dukae_BadFile; + duka_closeTrack (track); + return NULL; + } + + for (i = 0; i < track->cframes; ++i) + track->frames[i] = UQM_SwapBE32 (track->frames[i]); + + if (duka_readAudFrameHeader (track, 0, &aud) < 0) + { + duka_closeTrack (track); + return NULL; + } + + track->rate = 22050; + track->channels = 2; + track->pcm_frame = aud.numsamples; + track->data = HMalloc (DATA_BUF_SIZE); + track->maxdata = DATA_BUF_SIZE; + + // estimate + track->length = (float) track->cframes / DUCK_GENERAL_FPS; + + duka_error = 0; + + return track; +} + +void +duka_closeTrack (DukAud_Track* track) +{ + if (track->data) + HFree (track->data); + if (track->duk) + uio_fclose (track->duk); + HFree (track); + duka_error = 0; +} + +bool +duka_seekTrack (DukAud_Track* track, float pos /* in seconds */) +{ + if (pos < 0 || pos > track->length) + return false; + + track->iframe = (uint32) (pos * DUCK_GENERAL_FPS); + track->cbdata = 0; + track->dataofs = 0; + track->predictors[0] = 0; + track->predictors[1] = 0; + + return true; +} + +// This table is from one of the files that came with the original 3do source +// It's slightly different from the data used by MPlayer. +static int adpcm_step[89] = { + 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xF, + 0x10, 0x12, 0x13, 0x15, 0x17, 0x1A, 0x1C, 0x1F, + 0x22, 0x26, 0x29, 0x2E, 0x32, 0x37, 0x3D, 0x43, + 0x4A, 0x51, 0x59, 0x62, 0x6C, 0x76, 0x82, 0x8F, + 0x9E, 0xAD, 0xBF, 0xD2, 0xE7, 0xFE, 0x117, 0x133, + 0x152, 0x174, 0x199, 0x1C2, 0x1EF, 0x220, 0x256, 0x292, + 0x2D4, 0x31D, 0x36C, 0x3C4, 0x424, 0x48E, 0x503, 0x583, + 0x610, 0x6AC, 0x756, 0x812, 0x8E1, 0x9C4, 0xABE, 0xBD1, + 0xCFF, 0xE4C, 0xFBA, 0x114D, 0x1308, 0x14EF, 0x1707, 0x1954, + 0x1BDD, 0x1EA6, 0x21B7, 0x2516, + 0x28CB, 0x2CDF, 0x315C, 0x364C, + 0x3BBA, 0x41B2, 0x4844, 0x4F7E, + 0x5771, 0x6030, 0x69CE, 0x7463, + 0x7FFF + }; + + +// *** BEGIN part copied from MPlayer *** +// (some little changes) + +#if 0 +// pertinent tables for IMA ADPCM +static int adpcm_step[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 + }; +#endif + +static int adpcm_index[16] = { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 + }; + +// clamp a number between 0 and 88 +#define CLAMP_0_TO_88(x) \ + if ((x) < 0) (x) = 0; else if ((x) > 88) (x) = 88; + +// clamp a number within a signed 16-bit range +#define CLAMP_S16(x) \ + if ((x) < -32768) \ + (x) = -32768; \ + else if ((x) > 32767) \ + (x) = 32767; + +static void +decode_nibbles (sint16 *output, sint32 output_size, sint32 channels, + sint32* predictors, uint16* indices) +{ + sint32 step[2]; + sint32 index[2]; + sint32 diff; + sint32 i; + int sign; + sint32 delta; + int channel_number = 0; + + channels -= 1; + index[0] = indices[0]; + index[1] = indices[1]; + step[0] = adpcm_step[index[0]]; + step[1] = adpcm_step[index[1]]; + + for (i = 0; i < output_size; i++) + { + delta = output[i]; + + index[channel_number] += adpcm_index[delta]; + CLAMP_0_TO_88(index[channel_number]); + + sign = delta & 8; + delta = delta & 7; + +#if 0 + // fast approximation, used in most decoders + diff = step[channel_number] >> 3; + if (delta & 4) diff += step[channel_number]; + if (delta & 2) diff += step[channel_number] >> 1; + if (delta & 1) diff += step[channel_number] >> 2; +#else + // real thing +// diff = ((signed)delta + 0.5) * step[channel_number] / 4; + diff = (((delta << 1) + 1) * step[channel_number]) >> 3; +#endif + + if (sign) + predictors[channel_number] -= diff; + else + predictors[channel_number] += diff; + + CLAMP_S16(predictors[channel_number]); + output[i] = predictors[channel_number]; + step[channel_number] = adpcm_step[index[channel_number]]; + + // toggle channel + channel_number ^= channels; + } +} +// *** END part copied from MPlayer *** + +sint32 +duka_decodeFrame (DukAud_Track* track, DukAud_AudSubframe* header, + uint8* input) +{ + uint8* inend; + sint16* output; + sint16* outptr; + sint32 outputsize; + + outputsize = header->numsamples * 2 * sizeof (sint16); + outptr = output = (sint16*) ((uint8*)track->data + track->cbdata); + + for (inend = input + header->numsamples; input < inend; ++input) + { + *(outptr++) = *input >> 4; + *(outptr++) = *input & 0x0f; + } + + decode_nibbles (output, header->numsamples * 2, track->channels, + track->predictors, header->indices); + + track->cbdata += outputsize; + + return outputsize; +} + + +sint32 +duka_readNextFrame (DukAud_Track* track) +{ + DukAud_FrameHeader hdr; + DukAud_AudSubframe* aud; + uint8* p; + + uio_fseek (track->duk, track->frames[track->iframe], SEEK_SET); + if (uio_fread (&hdr, sizeof(hdr), 1, track->duk) != 1) + { + duka_error = errno; + return dukae_BadFile; + } + hdr.audsize = UQM_SwapBE32 (hdr.audsize); + + // dump encoded data at the end of the buffer aligned on 8-byte + p = ((uint8*)track->data + track->maxdata - ((hdr.audsize + 7) & (-8))); + if (uio_fread (p, 1, hdr.audsize, track->duk) != hdr.audsize) + { + duka_error = errno; + return dukae_BadFile; + } + aud = (DukAud_AudSubframe*) p; + p += sizeof(DukAud_AudSubframe); + + aud->magic = UQM_SwapBE16 (aud->magic); + if (aud->magic != 0xf77f) + return duka_error = dukae_BadFile; + + aud->numsamples = UQM_SwapBE16 (aud->numsamples); + aud->tag = UQM_SwapBE16 (aud->tag); + aud->indices[0] = UQM_SwapBE16 (aud->indices[0]); + aud->indices[1] = UQM_SwapBE16 (aud->indices[1]); + + track->iframe++; + + return duka_decodeFrame (track, aud, p); +} + +static sint32 +duka_stuffBuffer (DukAud_Track* track, void* buf, sint32 bufsize) +{ + sint32 dataleft; + + dataleft = track->cbdata - track->dataofs; + if (dataleft > 0) + { + if (dataleft > bufsize) + dataleft = bufsize & (-4); + memcpy (buf, (uint8*)track->data + track->dataofs, dataleft); + track->dataofs += dataleft; + } + + if (track->cbdata > 0 && track->dataofs >= track->cbdata) + track->cbdata = track->dataofs = 0; // reset for new data + + return dataleft; +} + +sint32 +duka_readData (DukAud_Track* track, void* buf, sint32 bufsize) +{ + sint32 stuffed; + sint32 total = 0; + + if (bufsize <= 0) + return duka_error = dukae_BadArg; + + do + { + stuffed = duka_stuffBuffer (track, buf, bufsize); + ((uint8*)buf) += stuffed; + bufsize -= stuffed; + total += stuffed; + + if (bufsize > 0 && track->iframe < track->cframes) + { + stuffed = duka_readNextFrame (track); + if (stuffed <= 0) + return stuffed; + } + } while (bufsize > 0 && track->iframe < track->cframes); + + return total; +} + +uint32 +duka_getFrame (DukAud_Track* track) +{ + // if there is nothing buffered return the actual current frame + // otherwise return previous + return track->dataofs == track->cbdata ? + track->iframe : track->iframe - 1; +} + +uint32 +duka_getFrameSize (DukAud_Track* track) +{ + return track->pcm_frame * track->channels * sizeof (uint16); +} diff --git a/sc2/src/sc2code/libs/sound/decoders/dukaud.h b/sc2/src/sc2code/libs/sound/decoders/dukaud.h new file mode 100644 index 000000000..c598199b8 --- /dev/null +++ b/sc2/src/sc2code/libs/sound/decoders/dukaud.h @@ -0,0 +1,67 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* .duk sound track decoder */ + +#ifndef DUKAUD_H +#define DUKAUD_H + +#include "port.h" +#include "types.h" +#include "uio.h" + +typedef struct +{ + // public read-only + uint32 iframe; // current frame index + uint32 cframes; // total count of frames + uint32 rate; // frequency + uint32 channels; // number of channels + uint32 pcm_frame; // samples per frame + float length; // length in seconds (estimated) + + // private + uio_Stream* duk; + uint32* frames; + // buffer + void* data; + uint32 maxdata; + uint32 cbdata; + uint32 dataofs; + // decoder stuff + sint32 predictors[2]; + +} DukAud_Track; + +typedef enum +{ + // positive values are the same as in errno + dukae_None = 0, + dukae_Unknown = -1, + dukae_BadFile = -2, + dukae_BadArg = -3, + dukae_Other = -1000, +} DukAud_Error; + +sint32 duka_getError (); +DukAud_Track* duka_openTrack (uio_DirHandle *dir, const char *file); +void duka_closeTrack (DukAud_Track* track); +bool duka_seekTrack (DukAud_Track* track, float pos /* in seconds */); +sint32 duka_readData (DukAud_Track* track, void* buf, sint32 bufsize); +uint32 duka_getFrame (DukAud_Track* track); +uint32 duka_getFrameSize (DukAud_Track* track); + +#endif // DUKAUD_H