#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
#include "common.c"
#include "ape.c"
#include "id3.c"
#include "aac.c"
#include "asf.c"
#include "mac.c"
#include "mp3.c"
#include "mp4.c"
#include "mpc.c"
#include "ogg.c"
#include "wav.c"
#include "flac.c"
#define FILTER_TYPE_INFO 0x01
#define FILTER_TYPE_TAGS 0x02
struct _types {
char *type;
char *suffix[15];
};
typedef struct {
char* type;
int (*get_tags)(PerlIO *infile, char *file, HV *info, HV *tags);
int (*get_fileinfo)(PerlIO *infile, char *file, HV *tags);
int (*find_frame)(PerlIO *infile, char *file, int offset);
} taghandler;
struct _types audio_types[] = {
{"mp4", {"mp4", "m4a", "m4b", "m4p", "m4v", "m4r", "k3g", "skm", "3gp", "3g2", "mov", 0}},
{"aac", {"aac", 0}},
{"mp3", {"mp3", "mp2", 0}},
{"ogg", {"ogg", "oga", 0}},
{"mpc", {"mpc", "mp+", "mpp", 0}},
{"ape", {"ape", "apl", 0}},
{"flc", {"flc", "flac", "fla", 0}},
{"asf", {"wma", "asf", "wmv", 0}},
{"wav", {"wav", "aif", "aiff", 0}},
{0, {0, 0}}
};
static taghandler taghandlers[] = {
{ "mp4", get_mp4tags, 0, mp4_find_frame },
{ "aac", get_aacinfo, 0, 0 },
{ "mp3", get_mp3tags, get_mp3fileinfo, mp3_find_frame },
{ "ogg", get_ogg_metadata, 0, ogg_find_frame },
{ "mpc", get_ape_metadata, get_mpcfileinfo, 0 },
{ "ape", get_ape_metadata, get_macfileinfo, 0 },
{ "flc", get_flac_metadata, 0, flac_find_frame },
{ "asf", get_asf_metadata, 0, asf_find_frame },
{ "wav", get_wav_metadata, 0, 0 },
{ NULL, 0, 0, 0 }
};
static taghandler *
_get_taghandler(char *suffix)
{
int typeindex = -1;
int i, j;
taghandler *hdl = NULL;
for (i=0; typeindex==-1 && audio_types[i].type; i++) {
for (j=0; typeindex==-1 && audio_types[i].suffix[j]; j++) {
#ifdef _MSC_VER
if (!stricmp(audio_types[i].suffix[j], suffix)) {
#else
if (!strcasecmp(audio_types[i].suffix[j], suffix)) {
#endif
typeindex = i;
break;
}
}
}
if (typeindex > -1) {
for (hdl = taghandlers; hdl->type; ++hdl)
if (!strcmp(hdl->type, audio_types[typeindex].type))
break;
}
return hdl;
}
HV *
_scan( char *suffix, PerlIO *infile, SV *path, uint8_t filter )
{
taghandler *hdl;
HV *out = newHV();
// don't leak
sv_2mortal( (SV*)out );
hdl = _get_taghandler(suffix);
if (hdl) {
HV *info = newHV();
// Ignore filter if a file type has only one function (FLAC/Ogg)
if ( !hdl->get_fileinfo ) {
filter = FILTER_TYPE_INFO | FILTER_TYPE_TAGS;
}
if ( hdl->get_fileinfo && (filter & FILTER_TYPE_INFO) ) {
hdl->get_fileinfo(infile, SvPVX(path), info);
}
if ( hdl->get_tags && (filter & FILTER_TYPE_TAGS) ) {
HV *tags = newHV();
hdl->get_tags(infile, SvPVX(path), info, tags);
hv_store( out, "tags", 4, newRV_noinc( (SV *)tags ), 0 );
}
// Info may be used in tag function, i.e. to find tag version
hv_store( out, "info", 4, newRV_noinc( (SV *)info ), 0 );
}
else {
croak("Audio::Scan unsupported file type: %s (%s)", suffix, SvPVX(path));
}
return out;
}
int
_find_frame( char *suffix, PerlIO *infile, SV *path, int offset )
{
int frame = -1;
taghandler *hdl = _get_taghandler(suffix);
if (hdl && hdl->find_frame) {
frame = hdl->find_frame(infile, SvPVX(path), offset);
}
return frame;
}
MODULE = Audio::Scan PACKAGE = Audio::Scan
int
has_flac(void)
CODE:
{
RETVAL = 1;
}
OUTPUT:
RETVAL
HV *
scan (char * /*klass*/, SV *path, ...)
CODE:
{
PerlIO *infile;
int filter = FILTER_TYPE_INFO | FILTER_TYPE_TAGS;
char *suffix = strrchr( SvPVX(path), '.' );
// Check for filter to only run one of the scan types
if ( items == 3 && SvOK(ST(2)) ) {
filter = SvIV(ST(2));
}
if ( !suffix ) {
XSRETURN_UNDEF;
}
suffix++;
if ( !(infile = PerlIO_open(SvPVX(path), "rb")) ) {
PerlIO_printf(PerlIO_stderr(), "Could not open %s for reading: %s\n", SvPVX(path), strerror(errno));
XSRETURN_UNDEF;
}
RETVAL = _scan( suffix, infile, path, filter );
PerlIO_close(infile);
}
OUTPUT:
RETVAL
HV *
scan_fh(char *, SV *type, SV *sfh, ...)
CODE:
{
uint8_t filter = FILTER_TYPE_INFO | FILTER_TYPE_TAGS;
char *suffix = SvPVX(type);
PerlIO *fh = IoIFP(sv_2io(sfh));
// Check for filter to only run one of the scan types
if ( items == 4 && SvOK(ST(3)) ) {
filter = SvIV(ST(3));
}
RETVAL = _scan( suffix, fh, newSVpv("(filehandle)", 0), filter );
}
OUTPUT:
RETVAL
int
find_frame(char *, SV *path, int offset)
CODE:
{
PerlIO *infile;
char *suffix = strrchr( SvPVX(path), '.' );
if ( !suffix ) {
RETVAL = -1;
return;
}
suffix++;
if ( !(infile = PerlIO_open(SvPVX(path), "rb")) ) {
PerlIO_printf(PerlIO_stderr(), "Could not open %s for reading\n", SvPVX(path));
RETVAL = -1;
return;
}
RETVAL = _find_frame( suffix, infile, path, offset );
PerlIO_close(infile);
}
OUTPUT:
RETVAL
int
find_frame_fh(char *, SV *type, SV *sfh, int offset)
CODE:
{
char *suffix = SvPVX(type);
PerlIO *fh = IoIFP(sv_2io(sfh));
RETVAL = _find_frame( suffix, fh, newSVpv("(filehandle)", 0), offset );
}
OUTPUT:
RETVAL
int
is_supported(char *, SV *path)
CODE:
{
char *suffix = strrchr( SvPVX(path), '.' );
if (suffix != NULL && *suffix == '.' && _get_taghandler(suffix + 1)) {
RETVAL = 1;
}
else {
RETVAL = 0;
}
}
OUTPUT:
RETVAL
SV *
type_for(char *, SV *suffix)
CODE:
{
taghandler *hdl = NULL;
char *suff = SvPVX(suffix);
if (suff == NULL || *suff == '\0') {
RETVAL = newSV(0);
}
else {
hdl = _get_taghandler(suff);
if (hdl == NULL) {
RETVAL = newSV(0);
}
else {
RETVAL = newSVpv(hdl->type, 0);
}
}
}
OUTPUT:
RETVAL
AV *
get_types(void)
CODE:
{
int i;
RETVAL = newAV();
sv_2mortal((SV*)RETVAL);
for (i = 0; audio_types[i].type; i++) {
av_push(RETVAL, newSVpv(audio_types[i].type, 0));
}
}
OUTPUT:
RETVAL
AV *
extensions_for(char *, SV *type)
CODE:
{
int i, j;
char *t = SvPVX(type);
RETVAL = newAV();
sv_2mortal((SV*)RETVAL);
for (i = 0; audio_types[i].type; i++) {
#ifdef _MSC_VER
if (!stricmp(audio_types[i].type, t)) {
#else
if (!strcasecmp(audio_types[i].type, t)) {
#endif
for (j = 0; audio_types[i].suffix[j]; j++) {
av_push(RETVAL, newSVpv(audio_types[i].suffix[j], 0));
}
break;
}
}
}
OUTPUT:
RETVAL