Added .duk audio decoder

(video playback support phase 2)


git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@1247 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
avolkov
2003-09-20 01:11:37 +00:00
parent 3decaeaf7a
commit d8a815db68
5 changed files with 644 additions and 2 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
uqm_SUBDIRS="mikmod" uqm_SUBDIRS="mikmod"
uqm_CFILES="decoder.c wav.c" uqm_CFILES="decoder.c wav.c dukaud.c"
+146 -1
View File
@@ -34,6 +34,7 @@
#include "wav.h" #include "wav.h"
#include "libs/sound/sound_common.h" #include "libs/sound/sound_common.h"
#include "mikmod/mikmod.h" #include "mikmod/mikmod.h"
#include "dukaud.h"
extern bool fileExists2(uio_DirHandle *dir, const char *fileName); extern bool fileExists2(uio_DirHandle *dir, const char *fileName);
// Can't '#include "libs/file.h"' due to some type conflicts. // 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_mod = "MikMod";
static char *decoder_info_ogg = "Ogg Vorbis"; static char *decoder_info_ogg = "Ogg Vorbis";
static char *decoder_info_nul = "None"; static char *decoder_info_nul = "None";
static char *decoder_info_duk = "DukAud";
TFB_DecoderFormats decoder_formats; TFB_DecoderFormats decoder_formats;
@@ -231,7 +233,7 @@ TFB_SoundDecoder* SoundDecoder_Load (uio_DirHandle *dir, char *filename,
mod->panflag = 1; mod->panflag = 1;
mod->wrap = 0; mod->wrap = 0;
mod->loop = 1; mod->loop = 1;
decoder = (TFB_SoundDecoder *) HMalloc (sizeof (TFB_SoundDecoder)); decoder = (TFB_SoundDecoder *) HMalloc (sizeof (TFB_SoundDecoder));
decoder->buffer = HMalloc (buffer_size); decoder->buffer = HMalloc (buffer_size);
decoder->buffer_size = buffer_size; decoder->buffer_size = buffer_size;
@@ -333,6 +335,43 @@ TFB_SoundDecoder* SoundDecoder_Load (uio_DirHandle *dir, char *filename,
return decoder; 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 else
{ {
fprintf (stderr, "SoundDecoder_Load(): %s file format is unsupported\n", filename); 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); //fprintf (stderr, "SoundDecoder_Decode(): decoded %d bytes from %s\n", decoded_bytes, decoder->filename);
return decoded_bytes; 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: case SOUNDDECODER_NULL:
{ {
uint32 max_bytes; uint32 max_bytes;
@@ -591,6 +661,11 @@ uint32 SoundDecoder_DecodeAll (TFB_SoundDecoder *decoder)
return decoded_bytes; return decoded_bytes;
break; break;
} }
case SOUNDDECODER_DUK:
{
fprintf (stderr, "SoundDecoder_DecodeAll(): unimplemented for duk\n");
break;
}
case SOUNDDECODER_NULL: case SOUNDDECODER_NULL:
{ {
decoder->error = SOUNDDECODER_OK; decoder->error = SOUNDDECODER_OK;
@@ -656,6 +731,24 @@ void SoundDecoder_Seek (TFB_SoundDecoder *decoder, uint32 seekTime)
decoder->error = SOUNDDECODER_OK; decoder->error = SOUNDDECODER_OK;
return; 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: case SOUNDDECODER_BUF:
{ {
uint32 pcm_pos = seekTime * decoder->frequency / 1000; uint32 pcm_pos = seekTime * decoder->frequency / 1000;
@@ -714,6 +807,13 @@ void SoundDecoder_Free (TFB_SoundDecoder *decoder)
HFree (vf); HFree (vf);
break; 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: case SOUNDDECODER_BUF:
break; break;
case SOUNDDECODER_NULL: case SOUNDDECODER_NULL:
@@ -750,6 +850,7 @@ float SoundDecoder_GetTime (TFB_SoundDecoder *decoder)
return 0.0f; return 0.0f;
} }
case SOUNDDECODER_OGG: case SOUNDDECODER_OGG:
case SOUNDDECODER_DUK:
case SOUNDDECODER_BUF: case SOUNDDECODER_BUF:
case SOUNDDECODER_NULL: 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;
}
@@ -79,6 +79,7 @@ enum
SOUNDDECODER_OGG, SOUNDDECODER_OGG,
SOUNDDECODER_NULL, SOUNDDECODER_NULL,
SOUNDDECODER_BUF, SOUNDDECODER_BUF,
SOUNDDECODER_DUK,
}; };
extern TFB_DecoderFormats decoder_formats; extern TFB_DecoderFormats decoder_formats;
@@ -90,6 +91,7 @@ TFB_SoundDecoder* SoundDecoder_Load (uio_DirHandle *dir,
uint32 SoundDecoder_Decode (TFB_SoundDecoder *decoder); uint32 SoundDecoder_Decode (TFB_SoundDecoder *decoder);
uint32 SoundDecoder_DecodeAll (TFB_SoundDecoder *decoder); uint32 SoundDecoder_DecodeAll (TFB_SoundDecoder *decoder);
float SoundDecoder_GetTime (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_Seek (TFB_SoundDecoder *decoder, uint32 pcm_pos);
void SoundDecoder_Rewind (TFB_SoundDecoder *decoder); void SoundDecoder_Rewind (TFB_SoundDecoder *decoder);
void SoundDecoder_Free (TFB_SoundDecoder *decoder); void SoundDecoder_Free (TFB_SoundDecoder *decoder);
@@ -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 <stdio.h>
#include <string.h>
#include <errno.h>
#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);
}
@@ -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