git-svn-id: https://vgmstream.svn.sourceforge.net/svnroot/vgmstream@385 51a99a44-fe44-0410-b1ba-c3e57ba2b86b
This commit is contained in:
halleyscometsw 2008-08-02 10:24:28 +00:00
parent 30c2a4e033
commit 90f0dccd9c
13 changed files with 394 additions and 64 deletions

View File

@ -26,7 +26,8 @@ LAYOUT_OBJS=layout/ast_blocked.o \
layout/str_snds_blocked.o \
layout/ws_aud_blocked.o \
layout/interleave_byte.o \
layout/mus_acm_layout.o
layout/mus_acm_layout.o \
layout/aix_layout.o
META_OBJS=meta/adx_header.o \
meta/afc_header.o \
@ -108,7 +109,8 @@ META_OBJS=meta/adx_header.o \
meta/ps2_vas.o \
meta/ps2_tec.o \
meta/ps2_enth.o \
meta/sdt.o
meta/sdt.o \
meta/aix.o
OBJECTS=vgmstream.o streamfile.o util.o $(CODING_OBJS) $(LAYOUT_OBJS) $(META_OBJS)

View File

@ -18,5 +18,6 @@ liblayout_la_SOURCES += str_snds_blocked.c
liblayout_la_SOURCES += ws_aud_blocked.c
liblayout_la_SOURCES += interleave_byte.c
liblayout_la_SOURCES += mus_acm_layout.c
liblayout_la_SOURCES += aix_layout.c
EXTRA_DIST = layout.h

87
src/layout/aix_layout.c Normal file
View File

@ -0,0 +1,87 @@
#include "layout.h"
#include "../vgmstream.h"
#include "../coding/coding.h"
void render_vgmstream_aix(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) {
int samples_written=0;
aix_codec_data *data = vgmstream->codec_data;
while (samples_written<sample_count) {
int samples_to_do;
int samples_this_block = data->sample_counts[data->current_segment];
int current_stream;
if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) {
data->current_segment = 1;
for (current_stream = 0; current_stream < data->stream_count; current_stream++)
{
int i;
reset_vgmstream(data->adxs[data->current_segment*data->stream_count+current_stream]);
/* carry over the history from the loop point */
for (i=0;i<2;i++)
{
data->adxs[1*data->stream_count+current_stream]->ch[i].adpcm_history1_32 =
data->adxs[0+current_stream]->ch[i].adpcm_history1_32;
data->adxs[1*data->stream_count+current_stream]->ch[i].adpcm_history2_32 =
data->adxs[0+current_stream]->ch[i].adpcm_history2_32;
}
}
vgmstream->samples_into_block = 0;
continue;
}
samples_to_do = vgmstream_samples_to_do(samples_this_block, 1, vgmstream);
/*printf("samples_to_do=%d,samples_this_block=%d,samples_written=%d,sample_count=%d\n",samples_to_do,samples_this_block,samples_written,sample_count);*/
if (samples_written+samples_to_do > sample_count)
samples_to_do=sample_count-samples_written;
if (samples_to_do == 0)
{
int i;
data->current_segment++;
/*printf("next %d, %d samples\n",data->current_file,data->files[data->current_file]->total_values/data->files[data->current_file]->info.channels);*/
for (current_stream = 0; current_stream < data->stream_count; current_stream++)
{
reset_vgmstream(data->adxs[data->current_segment*data->stream_count+current_stream]);
/* carry over the history from the previous segment */
for (i=0;i<2;i++)
{
data->adxs[data->current_segment*data->stream_count+current_stream]->ch[i].adpcm_history1_32 =
data->adxs[(data->current_segment-1)*data->stream_count+current_stream]->ch[i].adpcm_history1_32;
data->adxs[data->current_segment*data->stream_count+current_stream]->ch[i].adpcm_history2_32 =
data->adxs[(data->current_segment-1)*data->stream_count+current_stream]->ch[i].adpcm_history2_32;
}
}
vgmstream->samples_into_block = 0;
continue;
}
/*printf("decode %d samples file %d\n",samples_to_do,data->current_file);*/
if (samples_to_do > AIX_BUFFER_SIZE/2)
{
samples_to_do = AIX_BUFFER_SIZE/2;
}
for (current_stream = 0; current_stream < data->stream_count; current_stream++)
{
int i;
VGMSTREAM *adx = data->adxs[data->current_segment*data->stream_count+current_stream];
render_vgmstream(data->buffer,samples_to_do,adx);
for (i = 0; i < samples_to_do; i++)
{
buffer[(i+samples_written)*vgmstream->channels+current_stream*2] = data->buffer[i*2];
buffer[(i+samples_written)*vgmstream->channels+current_stream*2+1] = data->buffer[i*2+1];
}
}
samples_written += samples_to_do;
vgmstream->current_sample += samples_to_do;
vgmstream->samples_into_block+=samples_to_do;
}
}

View File

@ -32,4 +32,6 @@ void render_vgmstream_interleave_byte(sample * buffer, int32_t sample_count, VGM
void render_vgmstream_mus_acm(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream);
void render_vgmstream_aix(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream);
#endif

View File

@ -218,6 +218,10 @@
RelativePath=".\meta\aifc.c"
>
</File>
<File
RelativePath=".\meta\aix.c"
>
</File>
<File
RelativePath=".\meta\ast.c"
>
@ -618,6 +622,10 @@
<Filter
Name="Source Files"
>
<File
RelativePath=".\layout\aix_layout.c"
>
</File>
<File
RelativePath=".\layout\ast_blocked.c"
>

View File

@ -85,5 +85,6 @@ libmeta_la_SOURCES += ps2_vas.c
libmeta_la_SOURCES += ps2_tec.c
libmeta_la_SOURCES += ps2_enth.c
libmeta_la_SOURCES += sdt.c
libmeta_la_SOURCES += aix.c
EXTRA_DIST = meta.h

View File

@ -13,11 +13,29 @@ typedef struct _AIXSTREAMFILE
int stream_id;
} AIXSTREAMFILE;
static STREAMFILE *open_aix_with_STREAMFILE(STREAMFILE *file,off_t start_offset,int stream_id);
VGMSTREAM * init_vgmstream_aix(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamFileAIX = NULL;
STREAMFILE * streamFileADX = NULL;
char filename[260];
off_t *segment_offset = NULL;
int32_t *samples_in_segment = NULL;
int32_t sample_count;
int loop_flag = 0;
int32_t loop_start_sample,loop_end_sample;
aix_codec_data *data = NULL;
off_t first_AIXP;
off_t stream_list_offset;
off_t stream_list_end;
int stream_count,segment_count;
int sample_rate;
int i;
@ -25,56 +43,193 @@ VGMSTREAM * init_vgmstream_aix(STREAMFILE *streamFile) {
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("aix",filename_extension(filename))) goto fail;
streamFileADX = streamFile->open(streamFile,filenameWAV,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!streamFileWAV) {
/* try again, ucase */
for (i=strlen(filenameWAV);i>=0&&filenameWAV[i]!=DIRSEP;i--)
filenameWAV[i]=toupper(filenameWAV[i]);
if (read_32bitBE(0x0,streamFile) != 0x41495846 || /* "AIXF" */
read_32bitBE(0x08,streamFile) != 0x01000014 ||
read_32bitBE(0x0c,streamFile) != 0x00000800)
goto fail;
streamFileWAV = streamFile->open(streamFile,filenameWAV,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!streamFileWAV) goto fail;
first_AIXP = read_32bitBE(0x4,streamFile)+8;
segment_count = (uint16_t)read_16bitBE(0x18,streamFile);
stream_list_offset = 0x20+0x10*segment_count+0x10;
if (stream_list_offset >= first_AIXP)
goto fail;
if (segment_count < 1)
goto fail;
sample_rate = read_32bitBE(stream_list_offset+8,streamFile);
if (!check_sample_rate(sample_rate))
goto fail;
samples_in_segment = calloc(segment_count,sizeof(int32_t));
if (!samples_in_segment)
goto fail;
segment_offset = calloc(segment_count,sizeof(off_t));
if (!segment_offset)
goto fail;
for (i = 0; i < segment_count; i++)
{
segment_offset[i] = read_32bitBE(0x20+0x10*i+0,streamFile);
samples_in_segment[i] = read_32bitBE(0x20+0x10*i+0x08,streamFile);
/*printf("samples_in_segment[%d]=%d\n",i,samples_in_segment[i]);*/
/* all segments must have equal samplerate */
if (read_32bitBE(0x20+0x10*i+0x0c,streamFile) != sample_rate)
goto fail;
}
/* let the real initer do the parsing */
vgmstream = init_vgmstream_riff(streamFileWAV);
if (!vgmstream) goto fail;
if (segment_offset[0] != first_AIXP)
goto fail;
close_streamfile(streamFileWAV);
streamFileWAV = NULL;
stream_count = (uint8_t)read_8bit(stream_list_offset,streamFile);
if (stream_count < 1)
goto fail;
stream_list_end = stream_list_offset + 0x8 + stream_count * 8;
/* install loops */
if (!vgmstream->loop_flag) {
vgmstream->loop_flag = 1;
vgmstream->loop_ch = calloc(vgmstream->channels,
sizeof(VGMSTREAMCHANNEL));
if (!vgmstream->loop_ch) goto fail;
if (stream_list_end >= first_AIXP)
goto fail;
for (i = 0; i < stream_count; i++)
{
/* all streams must have same samplerate as segments */
if (read_32bitBE(stream_list_offset+8+i*8,streamFile)!=sample_rate)
goto fail;
/* all streams must be stereo */
if (read_8bit(stream_list_offset+8+i*8+4,streamFile)!=2)
goto fail;
}
vgmstream->loop_start_sample = read_32bitLE(0,streamFile);
vgmstream->loop_end_sample = read_32bitLE(4,streamFile);
vgmstream->meta_type = meta_RIFF_WAVE_POS;
/* check for existence of segments */
for (i = 0; i < segment_count; i++)
{
int j;
off_t AIXP_offset = segment_offset[i];
for (j = 0; j < stream_count; j++)
{
if (read_32bitBE(AIXP_offset,streamFile)!=0x41495850) /* "AIXP" */
goto fail;
if (read_8bit(AIXP_offset+8,streamFile)!=j)
goto fail;
AIXP_offset += read_32bitBE(AIXP_offset+4,streamFile)+8;
}
}
/*streamFileAIX = streamFile->open(streamFile,filename,sample_rate*0.0375*2/32*18segment_count);*/
streamFileAIX = streamFile->open(streamFile,filename,sample_rate*0.1*segment_count);
if (!streamFileAIX) goto fail;
data = malloc(sizeof(aix_codec_data));
if (!data) goto fail;
data->segment_count = segment_count;
data->stream_count = stream_count;
data->adxs = malloc(sizeof(STREAMFILE *)*segment_count*stream_count);
if (!data->adxs) goto fail;
for (i=0;i<segment_count*stream_count;i++) {
data->adxs[i] = NULL;
}
data->sample_counts = calloc(segment_count,sizeof(int32_t));
if (!data->sample_counts) goto fail;
memcpy(data->sample_counts,samples_in_segment,segment_count*sizeof(int32_t));
/* for each segment */
for (i = 0; i < segment_count; i++)
{
int j;
/* for each stream */
for (j = 0; j < stream_count; j++)
{
VGMSTREAM *adx;
/*printf("try opening segment %d/%d stream %d/%d %x\n",i,segment_count,j,stream_count,segment_offset[i]);*/
streamFileADX = open_aix_with_STREAMFILE(streamFileAIX,segment_offset[i],j);
if (!streamFileADX) goto fail;
adx = data->adxs[i*stream_count+j] = init_vgmstream_adx(streamFileADX);
if (!adx)
goto fail;
close_streamfile(streamFileADX); streamFileADX = NULL;
if (adx->num_samples != data->sample_counts[i] ||
adx->channels != 2 ||
adx->loop_flag != 0)
goto fail;
/* save start things so we can restart for seeking/looping */
/* copy the channels */
memcpy(adx->start_ch,adx->ch,sizeof(VGMSTREAMCHANNEL)*adx->channels);
/* copy the whole VGMSTREAM */
memcpy(adx->start_vgmstream,adx,sizeof(VGMSTREAM));
}
}
if (segment_count > 1)
{
loop_flag = 1;
}
sample_count = 0;
for (i = 0; i < segment_count; i++)
{
sample_count += data->sample_counts[i];
if (i == 0)
loop_start_sample = sample_count;
if (i == 1)
loop_end_sample = sample_count;
}
vgmstream = allocate_vgmstream(stream_count*2,loop_flag);
vgmstream->num_samples = sample_count;
vgmstream->sample_rate = sample_rate;
vgmstream->loop_start_sample = loop_start_sample;
vgmstream->loop_end_sample = loop_end_sample;
vgmstream->coding_type = coding_CRI_ADX;
vgmstream->layout_type = layout_aix;
vgmstream->meta_type = meta_AIX;
vgmstream->ch[0].streamfile = streamFileAIX;
data->current_segment = 0;
vgmstream->codec_data = data;
free(segment_offset);
return vgmstream;
/* clean up anything we may have opened */
fail:
if (streamFileWAV) close_streamfile(streamFileWAV);
if (streamFileAIX) close_streamfile(streamFileAIX);
if (streamFileADX) close_streamfile(streamFileADX);
if (vgmstream) close_vgmstream(vgmstream);
if (samples_in_segment) free(samples_in_segment);
if (segment_offset) free(segment_offset);
if (data) {
if (data->adxs)
{
int i;
for (i=0;i<data->segment_count*data->stream_count;i++)
if (data->adxs)
close_vgmstream(data->adxs[i]);
free(data->adxs);
}
if (data->sample_counts)
{
free(data->sample_counts);
}
free(data);
}
return NULL;
}
static size_t read_aix(AIXSTREAMFILE *streamfile,uint8_t *dest,off_t offset,size_t length)
{
size_t sz = 0;
/*printf("trying to read %x bytes from %x (str%d)\n",length,offset,streamfile->stream_id);*/
while (length > 0)
{
int read_something = 0;
if (offset >= logical_file_size)
{
return sz;
}
/* read the beginning of the requested block, if we can */
if (offset >= streamfile->current_logical_offset)
{
@ -135,11 +290,12 @@ static size_t read_aix(AIXSTREAMFILE *streamfile,uint8_t *dest,off_t offset,size
/* seek ye forwards */
while (!found_block) {
/*printf("seek looks at %x\n",streamfile->current_physical_offset);*/
switch (read_32bitBE(streamfile->current_physical_offset,
streamfile->real_file))
{
case 0x41495850: /* AIXP */
if (read_8bitBE(
if (read_8bit(
streamfile->current_physical_offset+8,
streamfile->real_file) ==
streamfile->stream_id)
@ -154,12 +310,20 @@ static size_t read_aix(AIXSTREAMFILE *streamfile,uint8_t *dest,off_t offset,size
{
streamfile->current_logical_offset +=
streamfile->current_block_size;
streamfile->current_physical_offset +=
read_32bitBE(
streamfile->current_physical_offset+0x04,
streamfile->real_file
);
}
else
{
found_block = 1;
}
}
if (!found_block)
{
streamfile->current_physical_offset +=
read_32bitBE(
streamfile->current_physical_offset+0x04,
streamfile->real_file
) + 8;
}
break;
@ -196,7 +360,7 @@ static size_t get_offset_aix(AIXSTREAMFILE *streamfile)
static void get_name_aix(AIXSTREAMFILE *streamfile,char *buffer,size_t length)
{
strncpy(buffer,length,"ARBITRARY.ADX");
strncpy(buffer,"ARBITRARY.ADX",length);
buffer[length-1]='\0';
}
@ -210,12 +374,13 @@ static STREAMFILE *open_aix_impl(AIXSTREAMFILE *streamfile,const char * const fi
if (!newfile)
return NULL;
memcpy(newfile,streamfile,sizeof(AIXSTREAMFILE));
return newfile;
return &newfile->sf;
}
static STREAMFILE *open_aix_with_STREAMFILE(STREAMFILE *file,off_t start_offset,int stream_id);
static STREAMFILE *open_aix_with_STREAMFILE(STREAMFILE *file,off_t start_offset,int stream_id)
{
AIXSTREAMFILE *streamfile = malloc(sizeof(AIXSTREAMFILE));
if (!streamfile)
return NULL;
@ -234,7 +399,7 @@ static STREAMFILE *open_aix_with_STREAMFILE(STREAMFILE *file,off_t start_offset,
#endif
streamfile->real_file = file;
streamfile->current_physicial_offset =
streamfile->current_physical_offset =
streamfile->start_physical_offset = start_offset;
streamfile->current_logical_offset = 0;
streamfile->current_block_size = 0;

View File

@ -183,4 +183,6 @@ VGMSTREAM * init_vgmstream_ps2_enth(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_sdt(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_aix(STREAMFILE * streamFile);
#endif

View File

@ -107,6 +107,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
init_vgmstream_ps2_tec,
init_vgmstream_ps2_enth,
init_vgmstream_sdt,
init_vgmstream_aix,
};
#define INIT_VGMSTREAM_FCNS (sizeof(init_vgmstream_fcns)/sizeof(init_vgmstream_fcns[0]))
@ -209,6 +210,17 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
acm_reset(data->files[i]);
}
}
if (vgmstream->layout_type==layout_aix) {
aix_codec_data *data = vgmstream->codec_data;
int i;
data->current_segment = 0;
for (i=0;i<data->segment_count*data->stream_count;i++)
{
reset_vgmstream(data->adxs[i]);
}
}
}
/* simply allocate memory for the VGMSTREAM and its channels */
@ -271,28 +283,6 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
int i,j;
if (!vgmstream) return;
for (i=0;i<vgmstream->channels;i++) {
if (vgmstream->ch[i].streamfile) {
close_streamfile(vgmstream->ch[i].streamfile);
/* Multiple channels might have the same streamfile. Find the others
* that are the same as this and clear them so they won't be closed
* again. */
for (j=0;j<vgmstream->channels;j++) {
if (i!=j && vgmstream->ch[j].streamfile ==
vgmstream->ch[i].streamfile) {
vgmstream->ch[j].streamfile = NULL;
}
}
vgmstream->ch[i].streamfile = NULL;
}
}
if (vgmstream->loop_ch) free(vgmstream->loop_ch);
if (vgmstream->start_ch) free(vgmstream->start_ch);
if (vgmstream->ch) free(vgmstream->ch);
/* the start_vgmstream is considered just data */
if (vgmstream->start_vgmstream) free(vgmstream->start_vgmstream);
#ifdef VGM_USE_VORBIS
if (vgmstream->coding_type==coding_ogg_vorbis) {
ogg_vorbis_codec_data *data = vgmstream->codec_data;
@ -348,6 +338,52 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
}
}
if (vgmstream->layout_type==layout_aix) {
aix_codec_data *data = vgmstream->codec_data;
if (data) {
if (data->adxs) {
int i;
for (i=0;i<data->segment_count*data->stream_count;i++) {
/* note that the AIX close_streamfile won't do anything but
* deallocate itself, there is only one open file and that
* is in vgmstream->ch[0].streamfile */
close_vgmstream(data->adxs[i]);
}
free(data->adxs);
}
if (data->sample_counts) {
free(data->sample_counts);
}
free(data);
}
}
/* now that the special cases have had their chance, clean up the standard items */
for (i=0;i<vgmstream->channels;i++) {
if (vgmstream->ch[i].streamfile) {
close_streamfile(vgmstream->ch[i].streamfile);
/* Multiple channels might have the same streamfile. Find the others
* that are the same as this and clear them so they won't be closed
* again. */
for (j=0;j<vgmstream->channels;j++) {
if (i!=j && vgmstream->ch[j].streamfile ==
vgmstream->ch[i].streamfile) {
vgmstream->ch[j].streamfile = NULL;
}
}
vgmstream->ch[i].streamfile = NULL;
}
}
if (vgmstream->loop_ch) free(vgmstream->loop_ch);
if (vgmstream->start_ch) free(vgmstream->start_ch);
if (vgmstream->ch) free(vgmstream->ch);
/* the start_vgmstream is considered just data */
if (vgmstream->start_vgmstream) free(vgmstream->start_vgmstream);
free(vgmstream);
}
@ -392,6 +428,9 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre
case layout_mus_acm:
render_vgmstream_mus_acm(buffer,sample_count,vgmstream);
break;
case layout_aix:
render_vgmstream_aix(buffer,sample_count,vgmstream);
break;
}
}
@ -750,10 +789,10 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
buffer+samples_written*vgmstream->channels,samples_to_do,
vgmstream->channels);
break;
#endif
case coding_ACM:
/* handled in its own layout, here to quiet compiler */
break;
#endif
}
}
@ -1095,6 +1134,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
case layout_mus_acm:
snprintf(temp,TEMPSIZE,"multiple ACM files, ACM blocked");
break;
case layout_aix:
snprintf(temp,TEMPSIZE,"AIX interleave, internally 18-byte interleaved");
break;
default:
snprintf(temp,TEMPSIZE,"INCONCEIVABLE");
}
@ -1134,6 +1176,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
case meta_ADX_05:
snprintf(temp,TEMPSIZE,"CRI ADX header type 05");
break;
case meta_AIX:
snprintf(temp,TEMPSIZE,"CRI AIX header");
break;
case meta_DSP_AGSC:
snprintf(temp,TEMPSIZE,"Retro Studios AGSC header");
break;

View File

@ -115,6 +115,7 @@ typedef enum {
#endif
layout_acm, /* dummy, let libacm handle layout */
layout_mus_acm, /* mus has multi-files to deal with */
layout_aix, /* CRI AIX's wheels within wheels */
} layout_t;
/* The meta type specifies how we know what we know about the file. We may know because of a header we read, some of it may have been guessed from filenames, etc. */
@ -147,6 +148,7 @@ typedef enum {
meta_ADX_03, /* ADX "type 03" */
meta_ADX_04, /* ADX "type 04" */
meta_ADX_05, /* ADX "type 05" */
meta_AIX, /* CRI AIX */
/* etc */
meta_NGC_ADPDTK, /* NGC DTK/ADP, no header (.adp) */
@ -390,6 +392,20 @@ typedef struct {
ACMStream **files;
} mus_acm_codec_data;
#define AIX_BUFFER_SIZE 0x1000
/* AIXery */
typedef struct {
sample buffer[AIX_BUFFER_SIZE];
int segment_count;
int stream_count;
int current_segment;
/* one per segment */
int32_t *sample_counts;
/* organized like:
* segment1_stream1, segment1_stream2, segment2_stream1, segment2_stream2*/
VGMSTREAM **adxs;
} aix_codec_data;
/* do format detection, return pointer to a usable VGMSTREAM, or NULL on failure */
VGMSTREAM * init_vgmstream(const char * const filename);

View File

@ -1,5 +1,5 @@
export SHELL = /bin/sh
export CFLAGS=-Wall -O3
export CFLAGS=-Wall -ggdb
export LDFLAGS=-lm -L../src -lvgmstream -lvorbisfile -lmpg123
export STRIP=strip
@ -7,7 +7,6 @@ export STRIP=strip
test: libvgmstream.a test.o
$(CC) test.o $(LDFLAGS) $(CFLAGS) -o test
$(STRIP) test
test.o: test.c
$(CC) $(CFLAGS) -c "-DVERSION=\"`../version.sh`\"" test.c -o test.o

View File

@ -102,6 +102,7 @@ gchar *vgmstream_exts [] = {
"tec",
"enth",
"sdt",
"aix",
/* terminator */
NULL
};

View File

@ -166,6 +166,7 @@ char * extension_list[] = {
"tec\0TEC Audio File (*.TEC)\0",
"enth\0ENTH Audio File (*.ENTH)\0",
"sdt\0SDT Audio File (*.SDT)\0",
"aix\0AIX Audio File (*.AIX)\0",
};
void about(HWND hwndParent) {