Merge branch 'm4a-feature' of https://github.com/smowton/rivendell into smowton-m4a-feature

This commit is contained in:
Fred Gleason 2015-03-09 18:44:06 -04:00
commit eee86034da
8 changed files with 505 additions and 1 deletions

View File

@ -78,6 +78,8 @@ AC_ARG_ENABLE(lame,[ --disable-lame disable MPEG Layer 3 encode suppor
[LAME_DISABLED=yes],[])
AC_ARG_ENABLE(flac,[ --disable-flac disable FLAC encode/decode support],
[FLAC_DISABLED=yes],[])
AC_ARG_ENABLE(mp4v2,[ --disable-mp4 disable M4A decode support],
[MP4V2_DISABLED=yes],[])
#
# Check for Qt
@ -208,6 +210,19 @@ if test -z $FLAC_DISABLED ; then
AC_CHECK_LIB(FLAC,FLAC__metadata_get_tags,[FLAC_METADATA_FOUND=yes],[])
fi
#
# Check for MP4V2
#
if test -z $MP4V2_DISABLED ; then
AC_CHECK_HEADER(mp4v2/mp4v2.h,[MP4V2_HEADER_FOUND=yes],[])
if test $MP4V2_HEADER_FOUND ; then
AC_CHECK_HEADER(neaacdec.h,[MP4V2_FOUND=yes],[])
if test $MP4V2_FOUND ; then
AC_DEFINE(HAVE_MP4_LIBS)
fi
fi
fi
#
# Set Hard Library Dependencies
#
@ -534,6 +549,11 @@ AC_MSG_NOTICE("| OggVorbis Encoding/Decoding Support ... No |")
else
AC_MSG_NOTICE("| OggVorbis Encoding/Decoding Support ... Yes |")
fi
if test -z $MP4V2_FOUND ; then
AC_MSG_NOTICE("| M4A Decoding Support ... No |")
else
AC_MSG_NOTICE("| M4A Decoding Support ... Yes |")
fi
AC_MSG_NOTICE("| |")
AC_MSG_NOTICE("| Optional Components: |")
if test -z $USING_PAM ; then

View File

@ -177,6 +177,7 @@ dist_librd_la_SOURCES = dbversion.h\
rdmeteraverage.cpp rdmeteraverage.h\
rdmixer.cpp rdmixer.h\
rdmonitor_config.cpp rdmonitor_config.h\
rdmp4.cpp rdmp4.h\
rdnownext.cpp rdnownext.h\
rdoneshot.cpp rdoneshot.h\
rdpam.cpp rdpam.h\

View File

@ -23,11 +23,13 @@
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>
#include <dlfcn.h>
#include <errno.h>
#include <unistd.h>
#include <sndfile.h>
#include <samplerate.h>
@ -40,6 +42,10 @@
#include <FLAC++/encoder.h>
#include <rdflacdecode.h>
#endif // HAVE_FLAC
#ifdef HAVE_MP4_LIBS
#include <mp4v2/mp4v2.h>
#include <neaacdec.h>
#endif // HAVE_MP4_LIBS
#include <id3/tag.h>
#include <qfile.h>
@ -307,6 +313,11 @@ RDAudioConvert::ErrorCode RDAudioConvert::Stage1Convert(const QString &srcfile,
delete wave;
return err;
case RDWaveFile::M4A:
err=Stage1M4A(dstfile,wave);
delete wave;
return err;
case RDWaveFile::Aiff:
case RDWaveFile::Unknown:
break;
@ -673,6 +684,165 @@ RDAudioConvert::ErrorCode RDAudioConvert::Stage1Mpeg(const QString &dstfile,
#endif // HAVE_MAD
}
// Based on libfaad's frontend/main.c, but using libmp4v2 for MP4 access.
RDAudioConvert::ErrorCode RDAudioConvert::Stage1M4A(const QString &dstfile,
RDWaveFile *wave)
{
#ifdef HAVE_MP4_LIBS
SNDFILE *sf_dst=NULL;
SF_INFO sf_dst_info;
MP4FileHandle f;
MP4TrackId audioTrack;
MP4SampleId firstSample, lastSample;
uint32_t aacBufSize, aacConfigSize;
uint8_t *aacBuf, *aacConfigBuffer;
NeAACDecHandle hDecoder;
NeAACDecConfigurationPtr config;
unsigned long foundSampleRate;
unsigned char foundChannels;
RDAudioConvert::ErrorCode ret = RDAudioConvert::ErrorOk;
if(!dlmp4.load()) {
return RDAudioConvert::ErrorFormatNotSupported;
}
//
// Open source
//
f = dlmp4.MP4Read(wave->getName());
if(f == MP4_INVALID_FILE_HANDLE)
return RDAudioConvert::ErrorNoSource;
audioTrack = dlmp4.getMP4AACTrack(f);
firstSample = 1;
lastSample = dlmp4.MP4GetTrackNumberOfSamples(f, audioTrack);
if(conv_start_point > 0) {
double startsecs = ((double)conv_start_point) / 1000;
MP4Timestamp startts = (MP4Timestamp)(startsecs * wave->getSamplesPerSec());
firstSample = dlmp4.MP4GetSampleIdFromTime(f, audioTrack, startts, /*need_sync=*/false);
if(firstSample == MP4_INVALID_SAMPLE_ID) {
ret = RDAudioConvert::ErrorInvalidSource;
goto out_mp4;
}
}
if(conv_end_point > 0) {
double stopsecs = ((double)conv_end_point) / 1000;
MP4Timestamp stopts = (MP4Timestamp)(stopsecs * wave->getSamplesPerSec());
lastSample = dlmp4.MP4GetSampleIdFromTime(f, audioTrack, stopts, /*need_sync=*/false);
if(lastSample == MP4_INVALID_SAMPLE_ID) {
ret = RDAudioConvert::ErrorInvalidSource;
goto out_mp4;
}
}
aacBufSize = dlmp4.MP4GetTrackMaxSampleSize(f, audioTrack);
aacBuf = (uint8_t*)malloc(aacBufSize);
if(!aacBufSize || !aacBuf) {
// Probably the source's fault for specifying a massive buffer.
ret = RDAudioConvert::ErrorInvalidSource;
goto out_mp4;
}
dlmp4.MP4GetTrackESConfiguration(f, audioTrack, &aacConfigBuffer, &aacConfigSize);
if(!aacConfigBuffer) {
ret = RDAudioConvert::ErrorInvalidSource;
goto out_mp4_buf;
}
//
// Open Destination
//
memset(&sf_dst_info,0,sizeof(sf_dst_info));
sf_dst_info.format=SF_FORMAT_WAV|SF_FORMAT_FLOAT;
sf_dst_info.channels=wave->getChannels();
sf_dst_info.samplerate=wave->getSamplesPerSec();
if((sf_dst=sf_open(dstfile,SFM_WRITE,&sf_dst_info))==NULL) {
ret = RDAudioConvert::ErrorNoDestination;
goto out_mp4_configbuf;
}
sf_command(sf_dst,SFC_SET_NORM_DOUBLE,NULL,SF_FALSE);
//
// Initialize Decoder
//
hDecoder = dlmp4.NeAACDecOpen();
config = dlmp4.NeAACDecGetCurrentConfiguration(hDecoder);
config->outputFormat = FAAD_FMT_FLOAT;
config->downMatrix = 1; // Downmix >2 channels to stereo.
if(!dlmp4.NeAACDecSetConfiguration(hDecoder, config)) {
ret = RDAudioConvert::ErrorInvalidSource;
goto out_decoder;
}
if(dlmp4.NeAACDecInit2(hDecoder, aacConfigBuffer, aacConfigSize, &foundSampleRate, &foundChannels) < 0) {
ret = RDAudioConvert::ErrorInvalidSource;
goto out_decoder;
}
if(foundSampleRate != wave->getSamplesPerSec() || foundChannels != wave->getChannels()) {
fprintf(stderr, "M4A header information inconsistent with actual file? Header: %u/%u; file: %lu/%u\n",
wave->getSamplesPerSec(), (unsigned)wave->getChannels(), foundSampleRate, (unsigned)foundChannels);
ret = RDAudioConvert::ErrorInvalidSource;
goto out_decoder;
}
//
// Decode
//
for(MP4SampleId i = firstSample; i <= lastSample; ++i) {
uint32_t aacBytes = aacBufSize;
if(!dlmp4.MP4ReadSample(f, audioTrack, i, &aacBuf, &aacBytes, 0, 0, 0, 0)) {
ret = RDAudioConvert::ErrorInvalidSource;
break;
}
NeAACDecFrameInfo frameInfo;
// The library docs are not clear about the lifetime or cleanup of sample_buffer.
// I hope it lives until the next NeAACDecDecode call, and is cleaned up by NeAACDecClose
void* sample_buffer = dlmp4.NeAACDecDecode(hDecoder, &frameInfo, aacBuf, aacBytes);
if(!sample_buffer) {
ret = RDAudioConvert::ErrorInvalidSource;
break;
}
UpdatePeak((const float*)sample_buffer, frameInfo.samples);
if(sf_write_float(sf_dst, (const float*)sample_buffer, frameInfo.samples) != (sf_count_t)frameInfo.samples) {
ret = RDAudioConvert::ErrorInternal;
break;
}
}
//
// Cleanup
//
out_decoder:
dlmp4.NeAACDecClose(hDecoder);
// out_sf:
sf_close(sf_dst);
out_mp4_configbuf:
free(aacConfigBuffer);
out_mp4_buf:
free(aacBuf);
out_mp4:
dlmp4.MP4Close(f, 0);
return ret;
#else
return RDAudioConvert::ErrorFormatNotSupported;
#endif
}
RDAudioConvert::ErrorCode RDAudioConvert::Stage1SndFile(const QString &dstfile,
SNDFILE *sf_src,

View File

@ -33,6 +33,9 @@
#ifdef HAVE_MAD
#include <mad.h>
#endif // HAVE_MAD
#include <rdmp4.h>
#include <qobject.h>
#include <rdwavedata.h>
@ -70,6 +73,8 @@ class RDAudioConvert : public QObject
RDWaveFile *wave);
RDAudioConvert::ErrorCode Stage1Mpeg(const QString &dstfile,
RDWaveFile *wave);
RDAudioConvert::ErrorCode Stage1M4A(const QString &dstfile,
RDWaveFile *wave);
RDAudioConvert::ErrorCode Stage1SndFile(const QString &dstfile,
SNDFILE *sf_src,
SF_INFO *sf_src_info);
@ -150,6 +155,9 @@ class RDAudioConvert : public QObject
int (*lame_encode_flush)(lame_global_flags *,unsigned char *,int);
int (*lame_set_bWriteVbrTag)(lame_global_flags *, int);
#endif // HAVE_LAME
#ifdef HAVE_MP4_LIBS
DLMP4 dlmp4;
#endif
};

108
lib/rdmp4.cpp Normal file
View File

@ -0,0 +1,108 @@
// rdmp4.cpp
//
// Helpers for dealing with MP4 files.
//
// (C) Copyright 2014 Christopher Smowton <chris@smowton.net>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU Library General Public License
// version 2 as published by the Free Software Foundation.
//
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//
#ifdef HAVE_MP4_LIBS
#include <rdmp4.h>
#include <string.h>
#include <dlfcn.h>
MP4TrackId DLMP4::getMP4AACTrack(MP4FileHandle f)
{
uint32_t nTracks = this->MP4GetNumberOfTracks(f, NULL, 0);
for(uint32_t trackIndex = 0; trackIndex < nTracks; ++trackIndex) {
MP4TrackId thisTrack = this->MP4FindTrackId(f, trackIndex);
const char* trackType = this->MP4GetTrackType(f, thisTrack);
if(trackType && !strcmp(trackType, MP4_AUDIO_TRACK_TYPE)) {
const char* dataName = this->MP4GetTrackMediaDataName(f, thisTrack);
// The M4A format is only currently useful for AAC in an M4A container:
if(dataName &&
(!strcasecmp(dataName, "mp4a")) &&
this->MP4GetTrackEsdsObjectTypeId(f, thisTrack) == MP4_MPEG4_AUDIO_TYPE) {
return thisTrack;
}
}
}
return MP4_INVALID_TRACK_ID;
}
#define check_dlsym(lval, libhandle, symname) \
*(void**)(&lval) = dlsym(libhandle, symname); \
if(!lval) return false;
bool DLMP4::load()
{
if(loadSuccess)
return true;
neaac_handle = dlopen("libfaad.so",RTLD_LAZY);
mp4_handle = dlopen("libmp4v2.so",RTLD_LAZY);
if(!neaac_handle || !mp4_handle)
return false;
check_dlsym(this->MP4Read, mp4_handle, "MP4Read");
check_dlsym(this->MP4GetTrackNumberOfSamples, mp4_handle, "MP4GetTrackNumberOfSamples");
check_dlsym(this->MP4GetSampleIdFromTime, mp4_handle, "MP4GetSampleIdFromTime");
check_dlsym(this->MP4GetTrackMaxSampleSize, mp4_handle, "MP4GetTrackMaxSampleSize");
check_dlsym(this->MP4GetTrackESConfiguration, mp4_handle, "MP4GetTrackESConfiguration");
check_dlsym(this->MP4ReadSample, mp4_handle, "MP4ReadSample");
check_dlsym(this->MP4GetTrackBitRate, mp4_handle, "MP4GetTrackBitRate");
check_dlsym(this->MP4GetTrackAudioChannels, mp4_handle, "MP4GetTrackAudioChannels");
check_dlsym(this->MP4GetTrackDuration, mp4_handle, "MP4GetTrackDuration");
check_dlsym(this->MP4ConvertFromTrackDuration, mp4_handle, "MP4ConvertFromTrackDuration");
check_dlsym(this->MP4GetTrackTimeScale, mp4_handle, "MP4GetTrackTimeScale");
check_dlsym(this->MP4GetNumberOfTracks, mp4_handle, "MP4GetNumberOfTracks");
check_dlsym(this->MP4FindTrackId, mp4_handle, "MP4FindTrackId");
check_dlsym(this->MP4GetTrackType, mp4_handle, "MP4GetTrackType");
check_dlsym(this->MP4GetTrackMediaDataName, mp4_handle, "MP4GetTrackMediaDataName");
check_dlsym(this->MP4GetTrackEsdsObjectTypeId, mp4_handle, "MP4GetTrackEsdsObjectTypeId");
check_dlsym(this->MP4TagsAlloc, mp4_handle, "MP4TagsAlloc");
check_dlsym(this->MP4TagsFetch, mp4_handle, "MP4TagsFetch");
check_dlsym(this->MP4TagsFree, mp4_handle, "MP4TagsFree");
check_dlsym(this->MP4Close, mp4_handle, "MP4Close");
check_dlsym(this->NeAACDecOpen, neaac_handle, "NeAACDecOpen");
check_dlsym(this->NeAACDecGetCurrentConfiguration, neaac_handle, "NeAACDecGetCurrentConfiguration");
check_dlsym(this->NeAACDecSetConfiguration, neaac_handle, "NeAACDecSetConfiguration");
check_dlsym(this->NeAACDecInit2, neaac_handle, "NeAACDecInit2");
check_dlsym(this->NeAACDecDecode, neaac_handle, "NeAACDecDecode");
check_dlsym(this->NeAACDecClose, neaac_handle, "NeAACDecClose");
loadSuccess = true;
return true;
}
#undef check_dlsym
#endif // HAVE_MP4_LIBS

97
lib/rdmp4.h Normal file
View File

@ -0,0 +1,97 @@
// rdmp4.h
//
// Helpers for dealing with MP4 files.
//
// (C) Copyright 2014 Christopher Smowton <chris@smowton.net>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU Library General Public License
// version 2 as published by the Free Software Foundation.
//
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//
#ifndef RDMP4_H
#define RDMP4_H
#ifdef HAVE_MP4_LIBS
#include <mp4v2/mp4v2.h>
#include <neaacdec.h>
// neaacdec.h defines "LC", as in "low-complexity AAC", which clashes with a Rivendell Command.
#undef LC
struct DLMP4 {
DLMP4() : loadSuccess(false) {}
void *neaac_handle;
void *mp4_handle;
bool loadSuccess;
// MP4v2 Functions
MP4FileHandle (*MP4Read) (const char* fileName);
MP4SampleId (*MP4GetTrackNumberOfSamples) (MP4FileHandle hFile, MP4TrackId trackId);
MP4SampleId (*MP4GetSampleIdFromTime) (MP4FileHandle hFile, MP4TrackId trackId, MP4Timestamp when, bool wantSyncSample);
uint32_t (*MP4GetTrackMaxSampleSize) (MP4FileHandle hFile, MP4TrackId trackId);
bool (*MP4GetTrackESConfiguration) (MP4FileHandle hFile, MP4TrackId trackId, uint8_t** ppConfig, uint32_t* pConfigSize);
bool (*MP4ReadSample) (
MP4FileHandle hFile,
MP4TrackId trackId,
MP4SampleId sampleId,
uint8_t** ppBytes,
uint32_t* pNumBytes,
MP4Timestamp* pStartTime,
MP4Duration* pDuration,
MP4Duration* pRenderingOffset,
bool* pIsSyncSample);
uint32_t (*MP4GetTrackBitRate) (MP4FileHandle hFile, MP4TrackId trackId);
int (*MP4GetTrackAudioChannels) (MP4FileHandle hFile, MP4TrackId trackId);
MP4Duration (*MP4GetTrackDuration) (MP4FileHandle hFile, MP4TrackId trackId);
uint64_t (*MP4ConvertFromTrackDuration) (
MP4FileHandle hFile,
MP4TrackId trackId,
MP4Duration duration,
uint32_t timeScale);
uint32_t (*MP4GetTrackTimeScale) (MP4FileHandle hFile, MP4TrackId trackId);
uint32_t (*MP4GetNumberOfTracks) (MP4FileHandle hFile, const char* type, uint8_t subType);
MP4TrackId (*MP4FindTrackId) (MP4FileHandle hFile, uint32_t trackIdx);
const char* (*MP4GetTrackType) (MP4FileHandle hFile, MP4TrackId);
const char* (*MP4GetTrackMediaDataName) (MP4FileHandle hFile, MP4TrackId);
uint8_t (*MP4GetTrackEsdsObjectTypeId) (MP4FileHandle hFile, MP4TrackId);
const MP4Tags* (*MP4TagsAlloc) (void);
bool (*MP4TagsFetch) (const MP4Tags* tags, MP4FileHandle hFile);
void (*MP4TagsFree) (const MP4Tags* tags);
void (*MP4Close) (MP4FileHandle hFile, uint32_t flags);
// libfaad / NeAACDec functions
NeAACDecHandle (*NeAACDecOpen) (void);
NeAACDecConfigurationPtr (*NeAACDecGetCurrentConfiguration) (NeAACDecHandle hDecoder);
unsigned char (*NeAACDecSetConfiguration) (NeAACDecHandle hDecoder, NeAACDecConfigurationPtr config);
char (*NeAACDecInit2)(NeAACDecHandle hDecoder,
unsigned char *pBuffer,
unsigned long SizeOfDecoderSpecificInfo,
unsigned long *samplerate,
unsigned char *channels);
void* (*NeAACDecDecode) (NeAACDecHandle hDecoder,
NeAACDecFrameInfo *hInfo,
unsigned char *buffer,
unsigned long buffer_size);
void (*NeAACDecClose) (NeAACDecHandle hDecoder);
// Helper functions:
MP4TrackId getMP4AACTrack(MP4FileHandle f);
bool load();
};
#endif // HAVE_MP4_LIBS
#endif // RDMP4_H

View File

@ -33,6 +33,7 @@
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include <id3/tag.h>
#include <id3/misc_support.h>
@ -47,6 +48,11 @@
#include <rd.h>
#include <rdwavefile.h>
#include <rdconf.h>
#include <rdmp4.h>
#ifdef HAVE_MP4_LIBS
#include <mp4v2/mp4v2.h>
#endif
RDWaveFile::RDWaveFile(QString file_name)
{
@ -322,6 +328,75 @@ bool RDWaveFile::openWave(RDWaveData *data)
ReadId3Metadata();
break;
case RDWaveFile::M4A:
{
#ifdef HAVE_MP4_LIBS
// MP4 libs must already be loaded by now for file to have that type.
assert(dlmp4.load());
format_tag=WAVE_FORMAT_M4A;
MP4FileHandle f = dlmp4.MP4Read(getName());
if(f == MP4_INVALID_FILE_HANDLE)
return false;
// Find an audio track, and populate sample rate, bits/sample etc.
MP4TrackId audioTrack = dlmp4.getMP4AACTrack(f);
if(audioTrack == MP4_INVALID_TRACK_ID) {
dlmp4.MP4Close(f, 0);
return false;
}
// Found audio track. Get audio data:
avg_bytes_per_sec = dlmp4.MP4GetTrackBitRate(f, audioTrack);
channels = dlmp4.MP4GetTrackAudioChannels(f, audioTrack);
MP4Duration trackDuration = dlmp4.MP4GetTrackDuration(f, audioTrack);
ext_time_length = (unsigned)dlmp4.MP4ConvertFromTrackDuration(f, audioTrack, trackDuration,
MP4_MSECS_TIME_SCALE);
time_length = ext_time_length / 1000;
samples_per_sec = dlmp4.MP4GetTrackTimeScale(f, audioTrack);
bits_per_sample = 16;
data_start = 0;
sample_length = dlmp4.MP4GetTrackNumberOfSamples(f, audioTrack);
data_length = sample_length * 2 * channels;
data_chunk = true;
format_chunk = true;
wave_type = RDWaveFile::M4A;
// Now extract metadata (title, artist, etc)
if(wave_data) {
const MP4Tags* tags = dlmp4.MP4TagsAlloc();
dlmp4.MP4TagsFetch(tags, f);
wave_data->setMetadataFound(true);
if(tags->name)
wave_data->setTitle(tags->name);
if(tags->artist)
wave_data->setArtist(tags->artist);
if(tags->composer)
wave_data->setComposer(tags->composer);
if(tags->album)
wave_data->setAlbum(tags->album);
dlmp4.MP4TagsFree(tags);
}
dlmp4.MP4Close(f, 0);
return true;
#else
return false;
#endif
break;
}
case RDWaveFile::Ogg:
#ifdef HAVE_VORBIS
format_tag=WAVE_FORMAT_VORBIS;
@ -2020,6 +2095,9 @@ RDWaveFile::Type RDWaveFile::GetType(int fd)
if(IsOgg(fd)) {
return RDWaveFile::Ogg;
}
if(IsM4A(fd)) {
return RDWaveFile::M4A;
}
if(IsMpeg(fd)) {
return RDWaveFile::Mpeg;
}
@ -2211,6 +2289,20 @@ bool RDWaveFile::IsAiff(int fd)
return true;
}
bool RDWaveFile::IsM4A(int fd)
{
#ifdef HAVE_MP4_LIBS
if(!dlmp4.load())
return false;
MP4FileHandle f = dlmp4.MP4Read(getName());
bool ret = f != MP4_INVALID_FILE_HANDLE;
if(ret)
dlmp4.MP4Close(f, 0);
return ret;
#else
return false;
#endif
}
off_t RDWaveFile::FindChunk(int fd,const char *chunk_name,unsigned *chunk_size,
bool big_end)

View File

@ -40,6 +40,8 @@
#include <vorbis/vorbisenc.h>
#endif // HAVE_VORBIS
#include <rdmp4.h>
#include <rdwavedata.h>
#include <rdringbuffer.h>
#include <rdsettings.h>
@ -116,7 +118,7 @@ class RDWaveFile
enum Format {Pcm8=0,Pcm16=1,Float32=2,MpegL1=3,MpegL2=4,MpegL3=5,
DolbyAc2=6,DolbyAc3=7,Vorbis=8};
enum Type {Unknown=0,Wave=1,Mpeg=2,Ogg=3,Atx=4,Tmc=5,Flac=6,Ambos=7,
Aiff=8};
Aiff=8,M4A=9};
enum MpegID {NonMpeg=0,Mpeg1=1,Mpeg2=2};
/**
@ -1046,6 +1048,7 @@ class RDWaveFile
bool IsTmc(int fd);
bool IsFlac(int fd);
bool IsAiff(int fd);
bool IsM4A(int fd);
off_t FindChunk(int fd,const char *chunk_name,unsigned *chunk_size,
bool big_end=false);
bool GetChunk(int fd,const char *chunk_name,unsigned *chunk_size,
@ -1240,6 +1243,10 @@ class RDWaveFile
ogg_page ogg_pg;
ogg_packet ogg_pack;
#endif // HAVE_VORBIS
#ifdef HAVE_MP4_LIBS
DLMP4 dlmp4;
#endif
};
@ -1378,6 +1385,7 @@ class RDWaveFile
*/
#define WAVE_FORMAT_VORBIS 0xFFFF
#define WAVE_FORMAT_FLAC 0xFFFE
#define WAVE_FORMAT_M4A 0xFFFD
/*
* Proprietary Format Categories