MP3: Replace MAD with minimp3

libMAD had memory safety issues, possibly with corrupt files. Hopefully,
this will fix the problem.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This commit is contained in:
Christopher Snowhill 2025-03-22 23:59:11 -07:00
parent 31281197d4
commit 7ff653b48f
14 changed files with 3714 additions and 1964 deletions

View file

@ -136,7 +136,6 @@
836FB5A718206F2500B3AD2D /* Hively.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 836FB5471820538800B3AD2D /* Hively.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
8370D73D277419F700245CE0 /* SQLiteStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 8370D73C277419F700245CE0 /* SQLiteStore.m */; };
8370D73F2775AE1300245CE0 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 8370D73E2775AE1300245CE0 /* libsqlite3.tbd */; };
8372C93D27C7895300E250C9 /* MAD.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8372C93027C785BE00E250C9 /* MAD.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
8377C66327B8CF6300E8BC0F /* SpectrumViewSK.m in Sources */ = {isa = PBXBuildFile; fileRef = 8377C66127B8CF6300E8BC0F /* SpectrumViewSK.m */; };
8377C6B927B900F000E8BC0F /* SpectrumItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 8377C6B827B900F000E8BC0F /* SpectrumItem.m */; };
837DC92B285B05710005C58A /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 837DC92A285B05710005C58A /* CoreData.framework */; };
@ -170,6 +169,7 @@
83B61E2429A8296500CD0580 /* LyricsWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83B61E2229A8296500CD0580 /* LyricsWindow.xib */; };
83B61E2829A82A0200CD0580 /* LyricsWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B61E2729A82A0200CD0580 /* LyricsWindowController.m */; };
83B72E3B279045B7006007A3 /* libfdk-aac.2.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83B72E2A279044F6006007A3 /* libfdk-aac.2.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
83B73B652D8FD75A00A57F08 /* minimp3.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83B73B602D8FC05A00A57F08 /* minimp3.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
83BC5AB220E4C87100631CD4 /* DualWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BC5AB020E4C87100631CD4 /* DualWindow.m */; };
83BC5ABF20E4CE7A00631CD4 /* InfoInspector.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17D1B0D00F6320EA00694C57 /* InfoInspector.xib */; };
83BC5AC020E4CE7D00631CD4 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17342A980D5FD20B00E8D854 /* MainMenu.xib */; };
@ -511,20 +511,6 @@
remoteGlobalIDString = 836FB52C1820538700B3AD2D;
remoteInfo = Hively;
};
8372C92F27C785BE00E250C9 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 8372C92A27C785BD00E250C9 /* MAD.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 8372C92327C785BD00E250C9;
remoteInfo = MAD;
};
8372C93B27C7893100E250C9 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 8372C92A27C785BD00E250C9 /* MAD.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = 8372C92227C785BD00E250C9;
remoteInfo = MAD;
};
8375B36117FFEF010092A79F /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 8359FF2C17FEF35C0060F3ED /* ArchiveSource.xcodeproj */;
@ -546,6 +532,20 @@
remoteGlobalIDString = 83B06686180D5668008E3612;
remoteInfo = MIDI;
};
83B73B5F2D8FC05A00A57F08 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 8372C92A27C785BD00E250C9 /* minimp3.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 8372C92327C785BD00E250C9;
remoteInfo = minimp3;
};
83B73B632D8FD74000A57F08 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 8372C92A27C785BD00E250C9 /* minimp3.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = 8372C92227C785BD00E250C9;
remoteInfo = minimp3;
};
83BB13C120E4E38E00723731 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 836F6B2518BDB80D0095E648 /* vgmstream.xcodeproj */;
@ -678,8 +678,8 @@
dstPath = "";
dstSubfolderSpec = 13;
files = (
83B73B652D8FD75A00A57F08 /* minimp3.bundle in CopyFiles */,
8327DBA9293CAD2400CD0580 /* Organya.bundle in CopyFiles */,
8372C93D27C7895300E250C9 /* MAD.bundle in CopyFiles */,
83489C6B2782F78700BDCEA2 /* libvgmPlayer.bundle in CopyFiles */,
834D794020E4EFEF00C4A5CC /* VorbisPlugin.bundle in CopyFiles */,
834D793F20E4EFEA00C4A5CC /* OpusPlugin.bundle in CopyFiles */,
@ -955,7 +955,7 @@
8370D739277419D200245CE0 /* SQLiteStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLiteStore.h; sourceTree = "<group>"; };
8370D73C277419F700245CE0 /* SQLiteStore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SQLiteStore.m; sourceTree = "<group>"; };
8370D73E2775AE1300245CE0 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
8372C92A27C785BD00E250C9 /* MAD.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = MAD.xcodeproj; path = Plugins/MAD/MAD.xcodeproj; sourceTree = "<group>"; };
8372C92A27C785BD00E250C9 /* minimp3.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = minimp3.xcodeproj; path = Plugins/minimp3/minimp3.xcodeproj; sourceTree = "<group>"; };
8375B05117FFEA400092A79F /* OpusPlugin.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OpusPlugin.xcodeproj; path = Plugins/Opus/OpusPlugin.xcodeproj; sourceTree = "<group>"; };
8377C66127B8CF6300E8BC0F /* SpectrumViewSK.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SpectrumViewSK.m; path = Visualization/SpectrumViewSK.m; sourceTree = "<group>"; };
8377C66227B8CF6300E8BC0F /* SpectrumViewSK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SpectrumViewSK.h; path = Visualization/SpectrumViewSK.h; sourceTree = "<group>"; };
@ -1270,7 +1270,7 @@
17C808830C3BD181005707C4 /* HTTPSource.xcodeproj */,
83489C4E2782F2DF00BDCEA2 /* libvgmPlayer.xcodeproj */,
8E8D40820CBB036600135C1B /* M3u.xcodeproj */,
8372C92A27C785BD00E250C9 /* MAD.xcodeproj */,
8372C92A27C785BD00E250C9 /* minimp3.xcodeproj */,
83B0669C180D5668008E3612 /* MIDI.xcodeproj */,
17C8089E0C3BD1AB005707C4 /* Musepack.xcodeproj */,
83E5EFAC1FFEF78100659F0F /* OpenMPT.xcodeproj */,
@ -1773,14 +1773,6 @@
name = Products;
sourceTree = "<group>";
};
8372C92B27C785BD00E250C9 /* Products */ = {
isa = PBXGroup;
children = (
8372C93027C785BE00E250C9 /* MAD.bundle */,
);
name = Products;
sourceTree = "<group>";
};
8377C66027B8CF2300E8BC0F /* Visualization */ = {
isa = PBXGroup;
children = (
@ -1824,6 +1816,14 @@
path = LyricsWindow;
sourceTree = "<group>";
};
83B73B5C2D8FC05A00A57F08 /* Products */ = {
isa = PBXGroup;
children = (
83B73B602D8FC05A00A57F08 /* minimp3.bundle */,
);
name = Products;
sourceTree = "<group>";
};
83BB13AE20E4E38E00723731 /* Products */ = {
isa = PBXGroup;
children = (
@ -1984,8 +1984,8 @@
buildRules = (
);
dependencies = (
83B73B642D8FD74000A57F08 /* PBXTargetDependency */,
8327DBA8293CAD0A00CD0580 /* PBXTargetDependency */,
8372C93C27C7893100E250C9 /* PBXTargetDependency */,
83489C6A2782F76900BDCEA2 /* PBXTargetDependency */,
ED69CBC625BE32B40090B90D /* PBXTargetDependency */,
834D793E20E4EFD200C4A5CC /* PBXTargetDependency */,
@ -2130,10 +2130,6 @@
ProductGroup = 8E8D40830CBB036600135C1B /* Products */;
ProjectRef = 8E8D40820CBB036600135C1B /* M3u.xcodeproj */;
},
{
ProductGroup = 8372C92B27C785BD00E250C9 /* Products */;
ProjectRef = 8372C92A27C785BD00E250C9 /* MAD.xcodeproj */;
},
{
ProductGroup = ED69CBB925BE328C0090B90D /* Products */;
ProjectRef = ED69CBB825BE328C0090B90D /* MASShortcut.xcodeproj */;
@ -2142,6 +2138,10 @@
ProductGroup = 83B0669D180D5668008E3612 /* Products */;
ProjectRef = 83B0669C180D5668008E3612 /* MIDI.xcodeproj */;
},
{
ProductGroup = 83B73B5C2D8FC05A00A57F08 /* Products */;
ProjectRef = 8372C92A27C785BD00E250C9 /* minimp3.xcodeproj */;
},
{
ProductGroup = 17C8089F0C3BD1AB005707C4 /* Products */;
ProjectRef = 17C8089E0C3BD1AB005707C4 /* Musepack.xcodeproj */;
@ -2357,13 +2357,6 @@
remoteRef = 836FB5461820538800B3AD2D /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
8372C93027C785BE00E250C9 /* MAD.bundle */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
path = MAD.bundle;
remoteRef = 8372C92F27C785BE00E250C9 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
83B066A1180D5669008E3612 /* MIDI.bundle */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
@ -2371,6 +2364,13 @@
remoteRef = 83B066A0180D5669008E3612 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
83B73B602D8FC05A00A57F08 /* minimp3.bundle */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
path = minimp3.bundle;
remoteRef = 83B73B5F2D8FC05A00A57F08 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
83BB13C220E4E38E00723731 /* vgmstream.bundle */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
@ -2748,11 +2748,6 @@
name = Hively;
targetProxy = 836FB5A518206F1500B3AD2D /* PBXContainerItemProxy */;
};
8372C93C27C7893100E250C9 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = MAD;
targetProxy = 8372C93B27C7893100E250C9 /* PBXContainerItemProxy */;
};
8375B36217FFEF010092A79F /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = ArchiveSource;
@ -2763,6 +2758,11 @@
name = MIDI;
targetProxy = 83B06702180D5776008E3612 /* PBXContainerItemProxy */;
};
83B73B642D8FD74000A57F08 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = minimp3;
targetProxy = 83B73B632D8FD74000A57F08 /* PBXContainerItemProxy */;
};
83BCB8D917FC96F800760340 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = HighlyComplete;

View file

@ -1,902 +0,0 @@
//
// MADFile.m
// Cog
//
// Created by Vincent Spader on 6/17/06.
// Copyright 2006 Vincent Spader. All rights reserved.
//
#import "MADDecoder.h"
#import "HTTPSource.h"
#import "Logging.h"
#import "id3tag.h"
#import <Accelerate/Accelerate.h>
#import "CVbriHeader.h"
@implementation MADDecoder
#define LAME_HEADER_SIZE ((8 * 5) + 4 + 4 + 8 + 32 + 16 + 16 + 4 + 4 + 8 + 12 + 12 + 8 + 8 + 2 + 3 + 11 + 32 + 32 + 32)
// From vbrheadersdk:
// ========================================
// A Xing header may be present in the ancillary
// data field of the first frame of an mp3 bitstream
// The Xing header (optionally) contains
// frames total number of audio frames in the bitstream
// bytes total number of bytes in the bitstream
// toc table of contents
// toc (table of contents) gives seek points
// for random access
// the ith entry determines the seek point for
// i-percent duration
// seek point in bytes = (toc[i]/256.0) * total_bitstream_bytes
// e.g. half duration seek point = (toc[50]/256.0) * total_bitstream_bytes
#define FRAMES_FLAG 0x0001
#define BYTES_FLAG 0x0002
#define TOC_FLAG 0x0004
#define VBR_SCALE_FLAG 0x0008
// Scan file quickly
- (void)bufferRefill:(struct mad_stream *)stream {
long bytesToRead, bytesRemaining;
if(NULL == stream->buffer || MAD_ERROR_BUFLEN == stream->error) {
if(stream->next_frame) {
bytesRemaining = stream->bufend - stream->next_frame;
memmove(_inputBuffer, stream->next_frame, bytesRemaining);
bytesToRead = INPUT_BUFFER_SIZE - bytesRemaining;
} else {
bytesToRead = INPUT_BUFFER_SIZE;
bytesRemaining = 0;
}
// Read raw bytes from the MP3 file
long bytesRead = [_source read:_inputBuffer + bytesRemaining amount:bytesToRead];
if(bytesRead == 0) {
memset(_inputBuffer + bytesRemaining + bytesRead, 0, MAD_BUFFER_GUARD);
bytesRead += MAD_BUFFER_GUARD;
inputEOF = YES;
}
mad_stream_buffer(stream, _inputBuffer, bytesRead + bytesRemaining);
stream->error = MAD_ERROR_NONE;
}
}
- (BOOL)scanFile {
struct mad_stream *stream;
struct mad_frame *frame;
stream = (struct mad_stream *) calloc(sizeof(struct mad_stream), 1);
frame = (struct mad_frame *) calloc(sizeof(struct mad_frame), 1);
if(!stream || !frame) {
ALog(@"Out of memory!");
return NO;
}
long framesDecoded = 0;
int samplesPerMPEGFrame = 0;
int id3_length = 0;
mad_stream_init(stream);
mad_frame_init(frame);
[_source seek:0 whence:SEEK_END];
_fileSize = [_source tell];
[_source seek:0 whence:SEEK_SET];
for(;;) {
[self bufferRefill:stream];
if(mad_frame_decode(frame, stream) == -1) {
if(MAD_RECOVERABLE(stream->error)) {
// Prevent ID3 tags from reporting recoverable frame errors
const uint8_t *buffer = stream->this_frame;
unsigned long buflen = stream->bufend - stream->this_frame;
if(8 <= buflen && 0x52 == buffer[0] && 0x49 == buffer[1] && 0x46 == buffer[2] &&
0x46 == buffer[3]) {
uint32_t riffLen = buffer[4] | (buffer[5] << 8) | (buffer[6] << 16) | (buffer[7] << 24);
if(riffLen + 8 == _fileSize ||
riffLen + 8 + 1 == _fileSize) { // In case of padding
ALog(@"Renamed RIFF file found, dropping to next input handler.");
return NO;
}
}
else if(10 <= buflen && 0x49 == buffer[0] && 0x44 == buffer[1] && 0x33 == buffer[2]) {
id3_length = (((buffer[6] & 0x7F) << (3 * 7)) | ((buffer[7] & 0x7F) << (2 * 7)) |
((buffer[8] & 0x7F) << (1 * 7)) | ((buffer[9] & 0x7F) << (0 * 7)));
_foundID3v2 = YES;
// Add 10 bytes for ID3 header
id3_length += 10;
void *tagBuffer = malloc(id3_length);
if(!tagBuffer) goto error;
memcpy(tagBuffer, &buffer[0], MIN(buflen, id3_length));
long bufleft = id3_length - buflen;
long tagRead = MIN(buflen, id3_length);
while(bufleft > 0) {
stream->error = MAD_ERROR_BUFLEN;
stream->next_frame = NULL;
[self bufferRefill:stream];
buffer = stream->this_frame;
buflen = stream->bufend - stream->this_frame;
memcpy(tagBuffer + tagRead, buffer, MIN(buflen, bufleft));
tagRead += MIN(buflen, bufleft);
bufleft -= buflen;
}
if(bufleft < 0) {
mad_stream_skip(stream, buflen + bufleft);
}
struct id3_tag *tag = id3_tag_parse(tagBuffer, id3_length);
if(tag) {
for(size_t i = 0; i < tag->nframes; ++i) {
struct id3_frame *frame = tag->frames[i];
if(!strcmp(frame->id, "COMM")) {
union id3_field *field;
const id3_ucs4_t *description;
const id3_ucs4_t *value;
field = id3_frame_field(frame, 2);
description = id3_field_getstring(field);
field = id3_frame_field(frame, 3);
value = id3_field_getfullstring(field);
if(description && value) {
id3_utf8_t *description8 = id3_ucs4_utf8duplicate(description);
if(!strcmp((const char *)description8, "iTunSMPB")) {
id3_utf8_t *value8 = id3_ucs4_utf8duplicate(value);
uint32_t zero, start_pad, end_pad;
uint64_t last_eight_frames_offset;
int64_t temp_duration;
if(sscanf((const char *)value8, "%" PRIx32 " %" PRIx32 " %" PRIx32 " %" PRIx64 " %" PRIx32 " %" PRIx64, &zero, &start_pad, &end_pad, &temp_duration, &zero, &last_eight_frames_offset) == 6 &&
temp_duration >= 0 &&
start_pad <= (576 * 2 * 32) &&
end_pad <= (576 * 2 * 64) &&
(_fileSize && (last_eight_frames_offset < (_fileSize - id3_length)))) {
if(end_pad >= 528 + 1) {
_startPadding = start_pad + 528 + 1;
_endPadding = end_pad - (528 + 1);
// iTunes encodes the original length of the file here
totalFrames = temp_duration + _startPadding + _endPadding;
_foundiTunSMPB = YES;
}
}
free(value8);
}
free(description8);
}
}
}
id3_tag_delete(tag);
}
free(tagBuffer);
} else if(stream->error == MAD_ERROR_BADDATAPTR) {
goto framedecoded;
}
continue;
} else if(stream->error == MAD_ERROR_BUFLEN && inputEOF) {
break;
} else if(stream->error == MAD_ERROR_BUFLEN) {
continue;
} else {
// DLog(@"Unrecoverable error: %s", mad_stream_errorstr(&stream));
break;
}
}
framedecoded:
framesDecoded++;
if(framesDecoded == 1) {
sampleRate = frame->header.samplerate;
channels = MAD_NCHANNELS(&frame->header);
if(MAD_FLAG_LSF_EXT & frame->header.flags || MAD_FLAG_MPEG_2_5_EXT & frame->header.flags) {
switch(frame->header.layer) {
case MAD_LAYER_I:
samplesPerMPEGFrame = 384;
layer = 1;
break;
case MAD_LAYER_II:
samplesPerMPEGFrame = 1152;
layer = 2;
break;
case MAD_LAYER_III:
samplesPerMPEGFrame = 576;
layer = 3;
break;
}
} else {
switch(frame->header.layer) {
case MAD_LAYER_I:
samplesPerMPEGFrame = 384;
layer = 1;
break;
case MAD_LAYER_II:
samplesPerMPEGFrame = 1152;
layer = 2;
break;
case MAD_LAYER_III:
samplesPerMPEGFrame = 1152;
layer = 3;
break;
}
}
if(layer != 3) continue;
const size_t ancillaryBitsRemaining = (stream->next_frame - stream->this_frame) * 8;
static const int64_t xing_offtbl[2][2] = {{32, 17}, {17,9}};
const int64_t xing_offset = xing_offtbl[!!(MAD_FLAG_LSF_EXT & frame->header.flags || MAD_FLAG_MPEG_2_5_EXT & frame->header.flags)][channels == 1] + 4; // Plus MPEG header
size_t ancBitsRemainingXing = ancillaryBitsRemaining - xing_offset * 8;
if(ancBitsRemainingXing >= 32) {
const uint8_t *ptr = stream->this_frame + xing_offset;
struct mad_bitptr bitptr;
mad_bit_init(&bitptr, ptr);
uint32_t magic = (uint32_t)mad_bit_read(&bitptr, 32);
ancBitsRemainingXing -= 32;
if('Xing' == magic || 'Info' == magic) {
unsigned i;
uint32_t flags = 0, frames = 0, bytes = 0, vbrScale = 0;
if(32 > ancBitsRemainingXing)
continue;
flags = (uint32_t)mad_bit_read(&bitptr, 32);
ancBitsRemainingXing -= 32;
// 4 byte value containing total frames
if(FRAMES_FLAG & flags) {
if(32 > ancBitsRemainingXing)
continue;
frames = (uint32_t)mad_bit_read(&bitptr, 32);
ancBitsRemainingXing -= 32;
// Determine number of samples, discounting encoder delay and padding
// Our concept of a frame is the same as CoreAudio's- one sample across all channels
totalFrames = frames * samplesPerMPEGFrame;
// DLog(@"TOTAL READ FROM XING");
}
// 4 byte value containing total bytes
if(BYTES_FLAG & flags) {
if(32 > ancBitsRemainingXing)
continue;
bytes = (uint32_t)mad_bit_read(&bitptr, 32);
ancBitsRemainingXing -= 32;
}
// 100 bytes containing TOC information
if(TOC_FLAG & flags) {
if(8 * 100 > ancBitsRemainingXing)
continue;
for(i = 0; i < 100; ++i)
/*_xingTOC[i] = */ mad_bit_read(&bitptr, 8);
ancBitsRemainingXing -= (8 * 100);
}
// 4 byte value indicating encoded vbr scale
if(VBR_SCALE_FLAG & flags) {
if(32 > ancBitsRemainingXing)
continue;
vbrScale = (uint32_t)mad_bit_read(&bitptr, 32);
ancBitsRemainingXing -= 32;
}
framesDecoded = frames;
_foundXingHeader = YES;
// Loook for the LAME header next
// http://gabriel.mp3-tech.org/mp3infotag.html
if(32 > ancBitsRemainingXing)
continue;
magic = (uint32_t)mad_bit_read(&bitptr, 32);
ancBitsRemainingXing -= 32;
if('LAME' == magic || 'Lavf' == magic || 'Lavc' == magic) {
if(LAME_HEADER_SIZE > ancBitsRemainingXing)
continue;
/*unsigned char versionString [5 + 1];
memset(versionString, 0, 6);*/
for(i = 0; i < 5; ++i)
/*versionString[i] =*/mad_bit_read(&bitptr, 8);
/*uint8_t infoTagRevision =*/mad_bit_read(&bitptr, 4);
/*uint8_t vbrMethod =*/mad_bit_read(&bitptr, 4);
/*uint8_t lowpassFilterValue =*/mad_bit_read(&bitptr, 8);
/*float peakSignalAmplitude =*/mad_bit_read(&bitptr, 32);
/*uint16_t radioReplayGain =*/mad_bit_read(&bitptr, 16);
/*uint16_t audiophileReplayGain =*/mad_bit_read(&bitptr, 16);
/*uint8_t encodingFlags =*/mad_bit_read(&bitptr, 4);
/*uint8_t athType =*/mad_bit_read(&bitptr, 4);
/*uint8_t lameBitrate =*/mad_bit_read(&bitptr, 8);
_startPadding = mad_bit_read(&bitptr, 12);
_endPadding = mad_bit_read(&bitptr, 12);
_startPadding += 528 + 1; // MDCT/filterbank delay
_endPadding -= 528 + 1;
/*uint8_t misc =*/mad_bit_read(&bitptr, 8);
/*uint8_t mp3Gain =*/mad_bit_read(&bitptr, 8);
/*DLog(@"Gain: %i", mp3Gain);*/
/*uint8_t unused =*/mad_bit_read(&bitptr, 2);
/*uint8_t surroundInfo =*/mad_bit_read(&bitptr, 3);
/*uint16_t presetInfo =*/mad_bit_read(&bitptr, 11);
/*uint32_t musicGain =*/mad_bit_read(&bitptr, 32);
/*uint32_t musicCRC =*/mad_bit_read(&bitptr, 32);
/*uint32_t tagCRC =*/mad_bit_read(&bitptr, 32);
ancBitsRemainingXing -= LAME_HEADER_SIZE;
_foundLAMEHeader = YES;
break;
}
}
}
const size_t vbri_offset = 4 + 32;
size_t ancBitsRemainingVBRI = ancillaryBitsRemaining - vbri_offset * 8;
if(ancBitsRemainingVBRI >= 32) {
const uint8_t *ptr = stream->this_frame + vbri_offset;
struct mad_bitptr bitptr;
mad_bit_init(&bitptr, ptr);
uint32_t magic = (uint32_t)mad_bit_read(&bitptr, 32);
ancBitsRemainingVBRI -= 32;
if('VBRI' == magic) {
struct VbriHeader *vbri_header = 0;
if(readVbriHeader(&vbri_header, mad_bit_nextbyte(&bitptr), ancBitsRemainingVBRI / 8) == 0) {
uint32_t frames = VbriTotalFrames(vbri_header);
totalFrames = frames * samplesPerMPEGFrame;
_startPadding = 0;
_endPadding = 0;
_foundVBRIHeader = YES;
}
if(vbri_header) {
freeVbriHeader(vbri_header);
}
break;
}
}
} else if(_foundXingHeader || _foundiTunSMPB || _foundVBRIHeader) {
break;
} else if(framesDecoded > 1) {
break;
}
}
// Don't commit division by zero on bad files
if(stream->next_frame == stream->this_frame) {
goto error;
}
if(!_foundiTunSMPB && !_foundXingHeader && !_foundVBRIHeader) {
// Now do CBR estimation instead of full file scanning
size_t frameCount = (_fileSize - id3_length) / (stream->next_frame - stream->this_frame);
mad_timer_t duration = frame->header.duration;
mad_timer_multiply(&duration, frameCount);
totalFrames = mad_timer_count(duration, sampleRate);
}
bitrate = ((double)((_fileSize - id3_length) * 8) / 1000.0) * (sampleRate / (double)totalFrames);
mad_frame_finish(frame);
mad_stream_finish(stream);
free(frame);
free(stream);
[_source seek:0 whence:SEEK_SET];
inputEOF = NO;
DLog(@"Mad properties: %@", [self properties]);
return YES;
error:
if(frame) {
mad_frame_finish(frame);
free(frame);
}
if(stream) {
mad_stream_finish(stream);
free(stream);
}
return NO;
}
- (BOOL)open:(id<CogSource>)source {
_source = source;
/* First the structures used by libmad must be initialized. */
mad_stream_init(&_stream);
mad_frame_init(&_frame);
mad_synth_init(&_synth);
_firstFrame = YES;
_outputFrames = 0;
_startPadding = 0;
_endPadding = 0;
// DLog(@"OPEN: %i", _firstFrame);
seconds = 0.0;
inputEOF = NO;
genre = @"";
album = @"";
artist = @"";
title = @"";
if(![_source seekable]) {
// Decode the first frame to get the channels, samplerate, etc.
int r;
do {
r = [self decodeMPEGFrame];
DLog(@"Decoding first frame: %i", r);
} while(r == 0);
if(r != -1) {
metadataUpdateInterval = sampleRate;
metadataUpdateCount = 0;
}
return (r == -1 ? NO : YES);
}
framesToSkip = 0;
BOOL ret = [self scanFile];
if(_foundLAMEHeader || _foundiTunSMPB) {
framesToSkip = _startPadding;
}
metadataUpdateInterval = sampleRate;
metadataUpdateCount = 0;
return ret;
}
- (BOOL)writeOutput {
unsigned long startingSample = 0;
unsigned long sampleCount = _synth.pcm.length;
// DLog(@"Position: %li/%li", _framesDecoded, totalFrames);
// DLog(@"<%i, %i>", _startPadding, _endPadding);
if(framesToSkip > 0) {
startingSample = framesToSkip;
}
// DLog(@"Counts: %i, %i", startingSample, sampleCount);
if(_foundLAMEHeader || _foundiTunSMPB) {
// Past the end of the file.
if(totalFrames - _endPadding <= _framesDecoded) {
// DLog(@"End of file. Not writing.");
return YES;
}
// Clip this for the following calculation, so this doesn't underflow
// when seeking and skipping a lot of samples
unsigned long startingSampleClipped = MIN(startingSample, sampleCount);
// We are at the end of the file and need to read the last few frames
if(_framesDecoded + (sampleCount - startingSampleClipped) > totalFrames - _endPadding) {
// DLog(@"End of file. %li", totalFrames - _endPadding - _framesDecoded);
sampleCount = totalFrames - _endPadding - _framesDecoded + startingSample;
}
} else {
// Past the end of the file.
if([_source seekable] && totalFrames <= _framesDecoded) {
return YES;
}
}
// We haven't even gotten to the start yet
if(startingSample >= sampleCount) {
// DLog(@"Skipping entire sample");
_framesDecoded += sampleCount;
framesToSkip -= sampleCount;
return NO;
}
framesToSkip = 0;
// DLog(@"Revised: %i, %i", startingSample, sampleCount);
_framesDecoded += sampleCount;
if(_outputFrames > 0) {
DLog(@"LOSING FRAMES!");
}
_outputFrames = (sampleCount - startingSample);
if(_currentOutputFrames < _outputFrames) {
_outputBuffer = (float *)realloc(_outputBuffer, _outputFrames * channels * sizeof(float));
_currentOutputFrames = _outputFrames;
}
int ch;
// samples [0 ... n]
for(ch = 0; ch < channels; ch++) {
vDSP_vflt32(&_synth.pcm.samples[ch][startingSample], 1, &_outputBuffer[ch], channels, _outputFrames);
}
float scale = (float)MAD_F_ONE;
vDSP_vsdiv(&_outputBuffer[0], 1, &scale, &_outputBuffer[0], 1, _outputFrames * channels);
// Output to a file
// FILE *f = fopen("data.raw", "a");
// fwrite(_outputBuffer, channels * 2, _outputFrames, f);
// fclose(f);
return NO;
}
- (int)decodeMPEGFrame {
if(_stream.buffer == NULL || _stream.error == MAD_ERROR_BUFLEN) {
int inputToRead;
int inputRemaining;
if(_stream.next_frame != NULL) {
inputRemaining = (int)(_stream.bufend - _stream.next_frame);
memmove(_inputBuffer, _stream.next_frame, inputRemaining);
inputToRead = INPUT_BUFFER_SIZE - inputRemaining;
} else {
inputToRead = INPUT_BUFFER_SIZE;
inputRemaining = 0;
}
long inputRead = [_source read:_inputBuffer + inputRemaining amount:INPUT_BUFFER_SIZE - inputRemaining];
if(inputRead == 0) {
memset(_inputBuffer + inputRemaining + inputRead, 0, MAD_BUFFER_GUARD);
inputRead += MAD_BUFFER_GUARD;
inputEOF = YES;
}
mad_stream_buffer(&_stream, _inputBuffer, inputRead + inputRemaining);
_stream.error = MAD_ERROR_NONE;
// DLog(@"Read stream.");
}
BOOL skippingBadFrame = NO;
if(mad_frame_decode(&_frame, &_stream) == -1) {
if(_stream.error == MAD_ERROR_BADDATAPTR) {
skippingBadFrame = YES;
} else if(MAD_RECOVERABLE(_stream.error)) {
const uint8_t *buffer = _stream.this_frame;
unsigned long buflen = _stream.bufend - _stream.this_frame;
uint32_t id3_length = 0;
// No longer need ID3Tag framework
if(10 <= buflen && 0x49 == buffer[0] && 0x44 == buffer[1] && 0x33 == buffer[2]) {
id3_length = (((buffer[6] & 0x7F) << (3 * 7)) | ((buffer[7] & 0x7F) << (2 * 7)) |
((buffer[8] & 0x7F) << (1 * 7)) | ((buffer[9] & 0x7F) << (0 * 7)));
// Add 10 bytes for ID3 header
id3_length += 10;
mad_stream_skip(&_stream, id3_length);
}
DLog(@"recoverable error");
return 0;
} else if(MAD_ERROR_BUFLEN == _stream.error && inputEOF) {
DLog(@"EOF");
return -1;
} else if(MAD_ERROR_BUFLEN == _stream.error) {
// DLog(@"Bufferlen");
return 0;
} else {
// DLog(@"Unrecoverable stream error: %s", mad_stream_errorstr(&_stream));
return -1;
}
}
if(!_firstFrame || !(_foundXingHeader && _foundVBRIHeader)) {
signed long frameDuration = mad_timer_count(_frame.header.duration, sampleRate);
if((framesToSkip - 1152 * 4) >= frameDuration) {
framesToSkip -= frameDuration;
_framesDecoded += frameDuration;
return 0;
}
}
// DLog(@"Decoded buffer.");
if(!skippingBadFrame) {
mad_synth_frame(&_synth, &_frame);
}
// DLog(@"first frame: %i", _firstFrame);
if(_firstFrame) {
_firstFrame = NO;
if(![_source seekable]) {
sampleRate = _frame.header.samplerate;
channels = MAD_NCHANNELS(&_frame.header);
switch(_frame.header.layer) {
case MAD_LAYER_I:
layer = 1;
break;
case MAD_LAYER_II:
layer = 2;
break;
case MAD_LAYER_III:
layer = 3;
break;
default:
break;
}
[self willChangeValueForKey:@"properties"];
[self didChangeValueForKey:@"properties"];
}
// DLog(@"FIRST FRAME!!! %i %i", _foundXingHeader, _foundLAMEHeader);
if(_foundXingHeader || _foundVBRIHeader) {
// DLog(@"Skipping xing header.");
return 0;
}
} else if(skippingBadFrame) {
return 0;
}
return 1;
}
- (BOOL)syncFormat {
float _sampleRate = _frame.header.samplerate;
int _channels = MAD_NCHANNELS(&_frame.header);
int _layer = 3;
switch(_frame.header.layer) {
case MAD_LAYER_I:
_layer = 1;
break;
case MAD_LAYER_II:
_layer = 2;
break;
case MAD_LAYER_III:
_layer = 3;
break;
default:
break;
}
BOOL changed = (_sampleRate != sampleRate ||
_channels != channels ||
_layer != layer);
if(changed) {
sampleRate = _sampleRate;
channels = _channels;
layer = _layer;
[self willChangeValueForKey:@"properties"];
[self didChangeValueForKey:@"properties"];
}
return changed;
}
- (AudioChunk *)readAudio {
if(!_firstFrame)
[self syncFormat];
id audioChunkClass = NSClassFromString(@"AudioChunk");
AudioChunk *chunk = nil;
for(;;) {
long framesToCopy = _outputFrames;
if(framesToCopy) {
chunk = [[audioChunkClass alloc] initWithProperties:[self properties]];
[chunk setStreamTimestamp:seconds];
[chunk assignSamples:_outputBuffer frameCount:framesToCopy];
seconds += [chunk duration];
_outputFrames = 0;
break;
}
int r = [self decodeMPEGFrame];
// DLog(@"Decoding frame: %i", r);
if(r == 0) // Recoverable error.
continue;
else if(r == -1) // Unrecoverable error
break;
if([self writeOutput]) {
return nil;
}
// DLog(@"Wrote output");
[self syncFormat];
}
metadataUpdateCount += chunk ? [chunk frameCount] : 0;
if(metadataUpdateCount >= metadataUpdateInterval) {
metadataUpdateCount -= metadataUpdateInterval;
[self updateMetadata];
}
// DLog(@"Read: %i/%i", bytesRead, size);
return chunk;
}
- (void)close {
if(_source) {
_source = nil;
}
if(_outputBuffer) {
free(_outputBuffer);
_outputBuffer = NULL;
_currentOutputFrames = 0;
}
mad_synth_finish(&_synth);
mad_frame_finish(&_frame);
mad_stream_finish(&_stream);
}
- (long)seek:(long)frame {
if(frame == _framesDecoded) {
return frame;
}
if(frame > totalFrames)
frame = totalFrames;
framesToSkip = 0;
if(_foundLAMEHeader || _foundiTunSMPB) {
if(_framesDecoded < _startPadding) {
framesToSkip = _startPadding;
}
}
if(frame < _framesDecoded) {
_framesDecoded = 0;
seconds = 0.0;
_firstFrame = YES;
if(_foundLAMEHeader || _foundiTunSMPB)
framesToSkip = _startPadding;
[_source seek:0 whence:SEEK_SET];
mad_stream_buffer(&_stream, NULL, 0);
}
framesToSkip += frame - _framesDecoded;
seconds += (double)(frame - _framesDecoded) / sampleRate;
return frame;
}
- (void)updateMetadata {
NSString *_artist = artist;
NSString *_album = album;
NSString *_title = title;
NSString *_genre = genre;
Class sourceClass = [_source class];
if([sourceClass isEqual:NSClassFromString(@"HTTPSource")]) {
HTTPSource *httpSource = (HTTPSource *)_source;
if([httpSource hasMetadata]) {
NSDictionary *metadata = [httpSource metadata];
_genre = [metadata valueForKey:@"genre"];
_album = [metadata valueForKey:@"album"];
_artist = [metadata valueForKey:@"artist"];
_title = [metadata valueForKey:@"title"];
}
}
if(![_artist isEqual:artist] ||
![_album isEqual:album] ||
![_title isEqual:title] ||
![_genre isEqual:genre]) {
artist = _artist;
album = _album;
title = _title;
genre = _genre;
if(![_source seekable]) {
[self willChangeValueForKey:@"metadata"];
[self didChangeValueForKey:@"metadata"];
}
}
}
- (NSDictionary *)properties {
if(layer < 1 || layer > 3) return nil;
const NSString *layers[3] = { @"MP1", @"MP2", @"MP3" };
return @{ @"channels": @(channels),
@"bitsPerSample": @(32),
@"sampleRate": @(sampleRate),
@"floatingPoint": @(YES),
@"bitrate": @(bitrate),
@"totalFrames": @(totalFrames - (_startPadding + _endPadding)),
@"seekable": @([_source seekable]),
@"codec": layers[layer - 1],
@"endian": @"host",
@"encoding": @"lossy" };
}
- (NSDictionary *)metadata {
return @{ @"artist": artist, @"album": album, @"title": title, @"genre": genre };
}
+ (NSArray *)fileTypes {
return @[@"mp3", @"m2a", @"mpa"];
}
+ (NSArray *)mimeTypes {
return @[@"audio/mpeg", @"audio/x-mp3"];
}
+ (NSArray *)fileTypeAssociations {
return @[@[@"MPEG Audio File", @"mp3.icns", @"mp3", @"m2a", @"mpa"]];
}
+ (float)priority {
return 2.0;
}
@end

View file

@ -1,5 +1,5 @@
//
// MADFile.h
// MP3Decoder.h
// Cog
//
// Created by Vincent Spader on 6/17/06.
@ -8,39 +8,39 @@
#import <Cocoa/Cocoa.h>
#import "MAD/mad.h"
#define MINIMP3_FLOAT_OUTPUT 1
#define MINIMP3_NO_STDIO 1
#import "ThirdParty/minimp3_ex.h"
#import "Plugin.h"
#define INPUT_BUFFER_SIZE 5 * 8192
@interface MADDecoder : NSObject <CogDecoder> {
struct mad_stream _stream;
struct mad_frame _frame;
struct mad_synth _synth;
@interface MP3Decoder : NSObject <CogDecoder> {
BOOL seekable;
mp3dec_t _decoder;
unsigned char _decoder_buffer[32768];
size_t _decoder_buffer_filled;
unsigned char _inputBuffer[INPUT_BUFFER_SIZE + MAD_BUFFER_GUARD];
float *_outputBuffer;
long _outputFrames;
long _currentOutputFrames;
long _fileSize;
mp3dec_ex_t _decoder_ex;
mp3dec_io_t _decoder_io;
mp3dec_frame_info_t _decoder_info;
size_t samples_filled;
mp3d_sample_t _decoder_buffer_output[MINIMP3_MAX_SAMPLES_PER_FRAME];
double seconds;
id<CogSource> _source;
BOOL _firstFrame;
// For gapless playback of mp3s
BOOL _foundXingHeader;
BOOL _foundLAMEHeader;
BOOL _foundVBRIHeader;
BOOL _foundID3v2;
uint32_t _startPadding, _endPadding;
BOOL _foundiTunSMPB;
long _fileSize;
long _framesDecoded;
uint16_t _startPadding;
uint16_t _endPadding;
BOOL _foundID3v2;
BOOL inputEOF;
@ -48,7 +48,6 @@
float sampleRate;
int bitrate;
long totalFrames;
long framesToSkip;
int layer;
int metadataUpdateInterval;
@ -60,6 +59,4 @@
NSString *title;
}
- (int)decodeMPEGFrame;
@end

View file

@ -0,0 +1,369 @@
//
// MP3Decoder.m
// Cog
//
// Created by Vincent Spader on 6/17/06.
// Copyright 2006 Vincent Spader. All rights reserved.
//
#define MINIMP3_IMPLEMENTATION 1
#import "MP3Decoder.h"
#import "HTTPSource.h"
#import "Logging.h"
#import "id3tag.h"
#import "CVbriHeader.h"
@implementation MP3Decoder
static size_t mp3_read_callback(void *buf, size_t size, void *user_data) {
id<CogSource> _file = (__bridge id<CogSource>)user_data;
return [_file read:buf amount:size];
}
static int mp3_seek_callback(uint64_t position, void *user_data) {
id<CogSource> _file = (__bridge id<CogSource>)user_data;
return [_file seek:position whence:SEEK_SET] ? 0 : -1;
}
- (BOOL)open:(id<CogSource>)source {
_source = source;
seekable = [source seekable];
size_t id3_length = 0;
if(seekable) {
uint8_t buffer[10];
size_t buflen = [source read:&buffer[0] amount:10];
[source seek:0 whence:SEEK_END];
_fileSize = [source tell];
[source seek:10 whence:SEEK_SET];
if(10 <= buflen && 0x49 == buffer[0] && 0x44 == buffer[1] && 0x33 == buffer[2]) {
id3_length = (((buffer[6] & 0x7F) << (3 * 7)) | ((buffer[7] & 0x7F) << (2 * 7)) |
((buffer[8] & 0x7F) << (1 * 7)) | ((buffer[9] & 0x7F) << (0 * 7)));
_foundID3v2 = YES;
// Add 10 bytes for ID3 header
id3_length += 10;
void *tagBuffer = malloc(id3_length);
if(!tagBuffer)
return NO;
memcpy(tagBuffer, &buffer[0], MIN(buflen, id3_length));
long bufleft = id3_length - buflen;
long tagRead = MIN(buflen, id3_length);
while(bufleft > 0) {
size_t bufRead = [source read:tagBuffer + tagRead amount:bufleft];
if(bufRead <= 0) {
free(tagBuffer);
return NO;
}
tagRead += bufRead;
if(bufRead < bufleft) {
free(tagBuffer);
return NO;
}
bufleft -= bufRead;
}
struct id3_tag *tag = id3_tag_parse(tagBuffer, id3_length);
if(tag) {
for(size_t i = 0; i < tag->nframes; ++i) {
struct id3_frame *frame = tag->frames[i];
if(!strcmp(frame->id, "COMM")) {
union id3_field *field;
const id3_ucs4_t *description;
const id3_ucs4_t *value;
field = id3_frame_field(frame, 2);
description = id3_field_getstring(field);
field = id3_frame_field(frame, 3);
value = id3_field_getfullstring(field);
if(description && value) {
id3_utf8_t *description8 = id3_ucs4_utf8duplicate(description);
if(!strcmp((const char *)description8, "iTunSMPB")) {
id3_utf8_t *value8 = id3_ucs4_utf8duplicate(value);
uint32_t zero, start_pad, end_pad;
uint64_t last_eight_frames_offset;
int64_t temp_duration;
if(sscanf((const char *)value8, "%" PRIx32 " %" PRIx32 " %" PRIx32 " %" PRIx64 " %" PRIx32 " %" PRIx64, &zero, &start_pad, &end_pad, &temp_duration, &zero, &last_eight_frames_offset) == 6 &&
temp_duration >= 0 &&
start_pad <= (576 * 2 * 32) &&
end_pad <= (576 * 2 * 64) &&
(_fileSize && (last_eight_frames_offset < (_fileSize - id3_length)))) {
if(end_pad >= 528 + 1) {
_startPadding = start_pad + 528 + 1;
_endPadding = end_pad - (528 + 1);
// iTunes encodes the original length of the file here
totalFrames = temp_duration;
_foundiTunSMPB = YES;
}
}
free(value8);
}
free(description8);
}
}
}
id3_tag_delete(tag);
}
free(tagBuffer);
}
_decoder_io.read = mp3_read_callback;
_decoder_io.read_data = (__bridge void *)source;
_decoder_io.seek = mp3_seek_callback;
_decoder_io.seek_data = (__bridge void *)source;
int error = mp3dec_ex_open_cb(&_decoder_ex, &_decoder_io, MP3D_SEEK_TO_SAMPLE);
if(error)
return NO;
if(_foundiTunSMPB) {
_decoder_ex.start_delay = _decoder_ex.to_skip = _startPadding * _decoder_ex.info.channels;
_decoder_ex.detected_samples = totalFrames * _decoder_ex.info.channels;
_decoder_ex.samples = (totalFrames + _startPadding + _endPadding) * _decoder_ex.info.channels;
}
mp3d_sample_t *sample_ptr = NULL;
size_t samples = mp3dec_ex_read_frame(&_decoder_ex, &sample_ptr, &_decoder_info, 1152 * 2);
if(samples && sample_ptr) {
samples_filled = samples / _decoder_info.channels;
memcpy(&_decoder_buffer_output[0], sample_ptr, sizeof(mp3d_sample_t) * samples);
}
inputEOF = NO;
if(!_foundiTunSMPB) {
size_t samples = _decoder_ex.detected_samples;
if(!samples) {
samples = _decoder_ex.samples;
}
totalFrames = samples / _decoder_info.channels;
}
bitrate = (double)((_fileSize - id3_length) * 8) / ((double)totalFrames / (double)_decoder_info.hz) / 1000.0;
} else {
_decoder_buffer_filled = [source read:_decoder_buffer amount:32768];
inputEOF = _decoder_buffer_filled < 32768;
mp3dec_init(&_decoder);
int samples = mp3dec_decode_frame(&_decoder, _decoder_buffer, (int)_decoder_buffer_filled, &_decoder_buffer_output[0], &_decoder_info);
if(!samples)
return NO;
size_t bytes_read = _decoder_info.frame_bytes;
if(bytes_read >= _decoder_buffer_filled) {
_decoder_buffer_filled = 0;
} else {
_decoder_buffer_filled -= bytes_read;
memmove(&_decoder_buffer[0], &_decoder_buffer[bytes_read], _decoder_buffer_filled);
}
samples_filled = samples;
bitrate = _decoder_info.bitrate_kbps;
}
[self syncFormat];
// DLog(@"OPEN: %i", _firstFrame);
seconds = 0.0;
genre = @"";
album = @"";
artist = @"";
title = @"";
metadataUpdateInterval = sampleRate;
metadataUpdateCount = 0;
return YES;
}
- (BOOL)syncFormat {
float _sampleRate = _decoder_info.hz;
int _channels = _decoder_info.channels;
int _layer = _decoder_info.layer;
BOOL changed = (_sampleRate != sampleRate ||
_channels != channels ||
_layer != layer);
if(changed) {
sampleRate = _sampleRate;
channels = _channels;
layer = _layer;
[self willChangeValueForKey:@"properties"];
[self didChangeValueForKey:@"properties"];
}
return changed;
}
- (AudioChunk *)readAudio {
id audioChunkClass = NSClassFromString(@"AudioChunk");
AudioChunk *chunk = nil;
for(;;) {
long framesToCopy = samples_filled;
if(framesToCopy) {
chunk = [[audioChunkClass alloc] initWithProperties:[self properties]];
[chunk setStreamTimestamp:seconds];
[chunk assignSamples:_decoder_buffer_output frameCount:framesToCopy];
seconds += [chunk duration];
samples_filled = 0;
_framesDecoded += framesToCopy;
break;
}
if(seekable) {
mp3d_sample_t *sample_ptr = NULL;
size_t samples = mp3dec_ex_read_frame(&_decoder_ex, &sample_ptr, &_decoder_info, 1152 * 2);
if(samples && sample_ptr) {
samples_filled = samples / _decoder_info.channels;
memcpy(&_decoder_buffer_output[0], sample_ptr, sizeof(mp3d_sample_t) * samples);
} else {
inputEOF = YES;
}
} else {
size_t bytesRemain = 32768 - _decoder_buffer_filled;
ssize_t bytesRead = [_source read:&_decoder_buffer[_decoder_buffer_filled] amount:bytesRemain];
if(bytesRead < 0 || bytesRead < bytesRemain) {
inputEOF = YES;
}
if(bytesRead > 0) {
_decoder_buffer_filled += bytesRead;
}
int samples = mp3dec_decode_frame(&_decoder, &_decoder_buffer[0], (int)_decoder_buffer_filled, &_decoder_buffer_output[0], &_decoder_info);
if(samples > 0) {
samples_filled = samples;
} else {
inputEOF = YES;
}
}
[self syncFormat];
if(inputEOF)
break;
}
metadataUpdateCount += chunk ? [chunk frameCount] : 0;
if(metadataUpdateCount >= metadataUpdateInterval) {
metadataUpdateCount -= metadataUpdateInterval;
[self updateMetadata];
}
// DLog(@"Read: %i/%i", bytesRead, size);
return chunk;
}
- (void)close {
if(_source) {
_source = nil;
}
}
- (long)seek:(long)frame {
if(frame == _framesDecoded) {
return frame;
}
if(frame > totalFrames)
frame = totalFrames;
if(seekable) {
int error = mp3dec_ex_seek(&_decoder_ex, frame);
if(error < 0) {
return -1;
}
}
seconds = (double)frame / (double)_decoder_info.hz;
_framesDecoded = frame;
return frame;
}
- (void)updateMetadata {
NSString *_artist = artist;
NSString *_album = album;
NSString *_title = title;
NSString *_genre = genre;
Class sourceClass = [_source class];
if([sourceClass isEqual:NSClassFromString(@"HTTPSource")]) {
HTTPSource *httpSource = (HTTPSource *)_source;
if([httpSource hasMetadata]) {
NSDictionary *metadata = [httpSource metadata];
_genre = [metadata valueForKey:@"genre"];
_album = [metadata valueForKey:@"album"];
_artist = [metadata valueForKey:@"artist"];
_title = [metadata valueForKey:@"title"];
}
}
if(![_artist isEqual:artist] ||
![_album isEqual:album] ||
![_title isEqual:title] ||
![_genre isEqual:genre]) {
artist = _artist;
album = _album;
title = _title;
genre = _genre;
if(![_source seekable]) {
[self willChangeValueForKey:@"metadata"];
[self didChangeValueForKey:@"metadata"];
}
}
}
- (NSDictionary *)properties {
if(layer < 1 || layer > 3) return nil;
const NSString *layers[3] = { @"MP1", @"MP2", @"MP3" };
return @{ @"channels": @(channels),
@"bitsPerSample": @(32),
@"sampleRate": @(sampleRate),
@"floatingPoint": @(YES),
@"bitrate": @(bitrate),
@"totalFrames": @(totalFrames),
@"seekable": @(seekable),
@"codec": layers[layer - 1],
@"endian": @"host",
@"encoding": @"lossy" };
}
- (NSDictionary *)metadata {
return @{ @"artist": artist, @"album": album, @"title": title, @"genre": genre };
}
+ (NSArray *)fileTypes {
return @[@"mp3", @"m2a", @"mpa"];
}
+ (NSArray *)mimeTypes {
return @[@"audio/mpeg", @"audio/x-mp3"];
}
+ (NSArray *)fileTypeAssociations {
return @[@[@"MPEG Audio File", @"mp3.icns", @"mp3", @"m2a", @"mpa"]];
}
+ (float)priority {
return 2.0;
}
@end

1865
Plugins/minimp3/ThirdParty/minimp3.h vendored Normal file

File diff suppressed because it is too large Load diff

1397
Plugins/minimp3/ThirdParty/minimp3_ex.h vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -7,8 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
8372C93527C7861300E250C9 /* MADDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 8372C93427C7861300E250C9 /* MADDecoder.m */; };
8372C93727C7863700E250C9 /* libmad.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8372C93627C7863700E250C9 /* libmad.a */; };
8372C93527C7861300E250C9 /* MP3Decoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 8372C93427C7861300E250C9 /* MP3Decoder.m */; };
8372C93F27C7904800E250C9 /* libid3tag.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8372C93E27C7904800E250C9 /* libid3tag.a */; };
8372C94227C7959000E250C9 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 8372C94127C7959000E250C9 /* libz.tbd */; };
83F97B6928600F9300A70B97 /* CVbriHeader.c in Sources */ = {isa = PBXBuildFile; fileRef = 83F97B6728600F9300A70B97 /* CVbriHeader.c */; };
@ -16,16 +15,17 @@
/* Begin PBXFileReference section */
834A42BB287AFB0700EB9D9B /* AudioChunk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AudioChunk.h; path = ../../Audio/Chain/AudioChunk.h; sourceTree = "<group>"; };
8372C92327C785BD00E250C9 /* MAD.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MAD.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
8372C93327C7861300E250C9 /* MADDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MADDecoder.h; sourceTree = "<group>"; };
8372C93427C7861300E250C9 /* MADDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MADDecoder.m; sourceTree = "<group>"; };
8372C93627C7863700E250C9 /* libmad.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmad.a; path = ../../ThirdParty/libmad/lib/libmad.a; sourceTree = "<group>"; };
8372C92327C785BD00E250C9 /* minimp3.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = minimp3.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
8372C93327C7861300E250C9 /* MP3Decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MP3Decoder.h; sourceTree = "<group>"; };
8372C93427C7861300E250C9 /* MP3Decoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MP3Decoder.m; sourceTree = "<group>"; };
8372C93827C7865A00E250C9 /* Plugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Plugin.h; path = ../../Audio/Plugin.h; sourceTree = "<group>"; };
8372C93927C7866B00E250C9 /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = "<group>"; };
8372C93A27C786DD00E250C9 /* HTTPSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTTPSource.h; path = ../HTTPSource/HTTPSource.h; sourceTree = "<group>"; };
8372C93E27C7904800E250C9 /* libid3tag.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libid3tag.a; path = ../../ThirdParty/libid3tag/lib/libid3tag.a; sourceTree = "<group>"; };
8372C94127C7959000E250C9 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
83747C272862DB9F0021245F /* Shared.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = "<group>"; };
83B73B612D8FC0A900A57F08 /* minimp3.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = minimp3.h; sourceTree = "<group>"; };
83B73B622D8FC0A900A57F08 /* minimp3_ex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = minimp3_ex.h; sourceTree = "<group>"; };
83F97B6728600F9300A70B97 /* CVbriHeader.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CVbriHeader.c; sourceTree = "<group>"; };
83F97B6828600F9300A70B97 /* CVbriHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CVbriHeader.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -36,7 +36,6 @@
buildActionMask = 2147483647;
files = (
8372C94227C7959000E250C9 /* libz.tbd in Frameworks */,
8372C93727C7863700E250C9 /* libmad.a in Frameworks */,
8372C93F27C7904800E250C9 /* libid3tag.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -53,10 +52,9 @@
8372C93A27C786DD00E250C9 /* HTTPSource.h */,
8372C93927C7866B00E250C9 /* Logging.h */,
8372C93827C7865A00E250C9 /* Plugin.h */,
8372C93327C7861300E250C9 /* MADDecoder.h */,
8372C93427C7861300E250C9 /* MADDecoder.m */,
8372C93327C7861300E250C9 /* MP3Decoder.h */,
8372C93427C7861300E250C9 /* MP3Decoder.m */,
8372C93E27C7904800E250C9 /* libid3tag.a */,
8372C93627C7863700E250C9 /* libmad.a */,
8372C92427C785BD00E250C9 /* Products */,
8372C94027C7959000E250C9 /* Frameworks */,
);
@ -65,7 +63,7 @@
8372C92427C785BD00E250C9 /* Products */ = {
isa = PBXGroup;
children = (
8372C92327C785BD00E250C9 /* MAD.bundle */,
8372C92327C785BD00E250C9 /* minimp3.bundle */,
);
name = Products;
sourceTree = "<group>";
@ -90,6 +88,8 @@
83F97B6628600F9300A70B97 /* ThirdParty */ = {
isa = PBXGroup;
children = (
83B73B612D8FC0A900A57F08 /* minimp3.h */,
83B73B622D8FC0A900A57F08 /* minimp3_ex.h */,
83F97B6728600F9300A70B97 /* CVbriHeader.c */,
83F97B6828600F9300A70B97 /* CVbriHeader.h */,
);
@ -99,9 +99,9 @@
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
8372C92227C785BD00E250C9 /* MAD */ = {
8372C92227C785BD00E250C9 /* minimp3 */ = {
isa = PBXNativeTarget;
buildConfigurationList = 8372C92727C785BD00E250C9 /* Build configuration list for PBXNativeTarget "MAD" */;
buildConfigurationList = 8372C92727C785BD00E250C9 /* Build configuration list for PBXNativeTarget "minimp3" */;
buildPhases = (
8372C91F27C785BD00E250C9 /* Sources */,
8372C92027C785BD00E250C9 /* Frameworks */,
@ -111,9 +111,9 @@
);
dependencies = (
);
name = MAD;
name = minimp3;
productName = MAD;
productReference = 8372C92327C785BD00E250C9 /* MAD.bundle */;
productReference = 8372C92327C785BD00E250C9 /* minimp3.bundle */;
productType = "com.apple.product-type.bundle";
};
/* End PBXNativeTarget section */
@ -130,7 +130,7 @@
};
};
};
buildConfigurationList = 8372C91E27C785BD00E250C9 /* Build configuration list for PBXProject "MAD" */;
buildConfigurationList = 8372C91E27C785BD00E250C9 /* Build configuration list for PBXProject "minimp3" */;
compatibilityVersion = "Xcode 13.0";
developmentRegion = en;
hasScannedForEncodings = 0;
@ -143,7 +143,7 @@
projectDirPath = "";
projectRoot = "";
targets = (
8372C92227C785BD00E250C9 /* MAD */,
8372C92227C785BD00E250C9 /* minimp3 */,
);
};
/* End PBXProject section */
@ -163,7 +163,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
8372C93527C7861300E250C9 /* MADDecoder.m in Sources */,
8372C93527C7861300E250C9 /* MP3Decoder.m in Sources */,
83F97B6928600F9300A70B97 /* CVbriHeader.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -351,7 +351,7 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
8372C91E27C785BD00E250C9 /* Build configuration list for PBXProject "MAD" */ = {
8372C91E27C785BD00E250C9 /* Build configuration list for PBXProject "minimp3" */ = {
isa = XCConfigurationList;
buildConfigurations = (
8372C92527C785BD00E250C9 /* Debug */,
@ -360,7 +360,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
8372C92727C785BD00E250C9 /* Build configuration list for PBXNativeTarget "MAD" */ = {
8372C92727C785BD00E250C9 /* Build configuration list for PBXNativeTarget "minimp3" */ = {
isa = XCConfigurationList;
buildConfigurations = (
8372C92827C785BD00E250C9 /* Debug */,

View file

@ -1,12 +0,0 @@
Built with the Homebrew defaults, sort of:
```
touch NEWS
touch AUTHORS
touch ChangeLog
autoreconf -fiv
./configure --disable-debugging --enable-fpm=64bit
make -j8 CFLAGS="-Os -arch x86_64 -arch arm64 -mmacosx-version-min=10.12" LDFLAGS="-arch x86_64 -arch arm64 -mmacosx-version-min=10.12"
```
Version 0.15.1b was used.

View file

@ -1,964 +0,0 @@
/*
* libmad - MPEG audio decoder library
* Copyright (C) 2000-2004 Underbit Technologies, Inc.
*
* 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
*
* If you would like to negotiate alternate licensing terms, you may do
* so by contacting: Underbit Technologies, Inc. <info@underbit.com>
*/
# ifdef __cplusplus
extern "C" {
# endif
# define FPM_64BIT
# define SIZEOF_INT 4
# define SIZEOF_LONG 8
# define SIZEOF_LONG_LONG 8
/* Id: version.h,v 1.26 2004/01/23 09:41:33 rob Exp */
# ifndef LIBMAD_VERSION_H
# define LIBMAD_VERSION_H
# define MAD_VERSION_MAJOR 0
# define MAD_VERSION_MINOR 15
# define MAD_VERSION_PATCH 1
# define MAD_VERSION_EXTRA " (beta)"
# define MAD_VERSION_STRINGIZE(str) #str
# define MAD_VERSION_STRING(num) MAD_VERSION_STRINGIZE(num)
# define MAD_VERSION MAD_VERSION_STRING(MAD_VERSION_MAJOR) "." \
MAD_VERSION_STRING(MAD_VERSION_MINOR) "." \
MAD_VERSION_STRING(MAD_VERSION_PATCH) \
MAD_VERSION_EXTRA
# define MAD_PUBLISHYEAR "2000-2004"
# define MAD_AUTHOR "Underbit Technologies, Inc."
# define MAD_EMAIL "info@underbit.com"
extern char const mad_version[];
extern char const mad_copyright[];
extern char const mad_author[];
extern char const mad_build[];
# endif
/* Id: fixed.h,v 1.38 2004/02/17 02:02:03 rob Exp */
# ifndef LIBMAD_FIXED_H
# define LIBMAD_FIXED_H
# if SIZEOF_INT >= 4
typedef signed int mad_fixed_t;
typedef signed int mad_fixed64hi_t;
typedef unsigned int mad_fixed64lo_t;
# else
typedef signed long mad_fixed_t;
typedef signed long mad_fixed64hi_t;
typedef unsigned long mad_fixed64lo_t;
# endif
# if defined(_MSC_VER)
# define mad_fixed64_t signed __int64
# elif 1 || defined(__GNUC__)
# define mad_fixed64_t signed long long
# endif
# if defined(FPM_FLOAT)
typedef double mad_sample_t;
# else
typedef mad_fixed_t mad_sample_t;
# endif
/*
* Fixed-point format: 0xABBBBBBB
* A == whole part (sign + 3 bits)
* B == fractional part (28 bits)
*
* Values are signed two's complement, so the effective range is:
* 0x80000000 to 0x7fffffff
* -8.0 to +7.9999999962747097015380859375
*
* The smallest representable value is:
* 0x00000001 == 0.0000000037252902984619140625 (i.e. about 3.725e-9)
*
* 28 bits of fractional accuracy represent about
* 8.6 digits of decimal accuracy.
*
* Fixed-point numbers can be added or subtracted as normal
* integers, but multiplication requires shifting the 64-bit result
* from 56 fractional bits back to 28 (and rounding.)
*
* Changing the definition of MAD_F_FRACBITS is only partially
* supported, and must be done with care.
*/
# define MAD_F_FRACBITS 28
# if MAD_F_FRACBITS == 28
# define MAD_F(x) ((mad_fixed_t) (x##L))
# else
# if MAD_F_FRACBITS < 28
# warning "MAD_F_FRACBITS < 28"
# define MAD_F(x) ((mad_fixed_t) \
(((x##L) + \
(1L << (28 - MAD_F_FRACBITS - 1))) >> \
(28 - MAD_F_FRACBITS)))
# elif MAD_F_FRACBITS > 28
# error "MAD_F_FRACBITS > 28 not currently supported"
# define MAD_F(x) ((mad_fixed_t) \
((x##L) << (MAD_F_FRACBITS - 28)))
# endif
# endif
# define MAD_F_MIN ((mad_fixed_t) -0x80000000L)
# define MAD_F_MAX ((mad_fixed_t) +0x7fffffffL)
# define MAD_F_ONE MAD_F(0x10000000)
# define mad_f_tofixed(x) ((mad_fixed_t) \
((x) * (double) (1L << MAD_F_FRACBITS) + 0.5))
# define mad_f_todouble(x) ((double) \
((x) / (double) (1L << MAD_F_FRACBITS)))
# define mad_f_intpart(x) ((x) >> MAD_F_FRACBITS)
# define mad_f_fracpart(x) ((x) & ((1L << MAD_F_FRACBITS) - 1))
/* (x should be positive) */
# define mad_f_fromint(x) ((x) << MAD_F_FRACBITS)
# define mad_f_add(x, y) ((x) + (y))
# define mad_f_sub(x, y) ((x) - (y))
# if defined(FPM_FLOAT)
# error "FPM_FLOAT not yet supported"
# undef MAD_F
# define MAD_F(x) mad_f_todouble(x)
# define mad_f_mul(x, y) ((x) * (y))
# define mad_f_scale64
# undef ASO_ZEROCHECK
# elif defined(FPM_64BIT)
/*
* This version should be the most accurate if 64-bit types are supported by
* the compiler, although it may not be the most efficient.
*/
# if defined(OPT_ACCURACY)
# define mad_f_mul(x, y) \
((mad_fixed_t) \
((((mad_fixed64_t) (x) * (y)) + \
(1L << (MAD_F_SCALEBITS - 1))) >> MAD_F_SCALEBITS))
# else
# define mad_f_mul(x, y) \
((mad_fixed_t) (((mad_fixed64_t) (x) * (y)) >> MAD_F_SCALEBITS))
# endif
# define MAD_F_SCALEBITS MAD_F_FRACBITS
/* --- Intel --------------------------------------------------------------- */
# elif defined(FPM_INTEL)
# if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4035) /* no return value */
static __forceinline
mad_fixed_t mad_f_mul_inline(mad_fixed_t x, mad_fixed_t y)
{
enum {
fracbits = MAD_F_FRACBITS
};
__asm {
mov eax, x
imul y
shrd eax, edx, fracbits
}
/* implicit return of eax */
}
# pragma warning(pop)
# define mad_f_mul mad_f_mul_inline
# define mad_f_scale64
# else
/*
* This Intel version is fast and accurate; the disposition of the least
* significant bit depends on OPT_ACCURACY via mad_f_scale64().
*/
# define MAD_F_MLX(hi, lo, x, y) \
asm ("imull %3" \
: "=a" (lo), "=d" (hi) \
: "%a" (x), "rm" (y) \
: "cc")
# if defined(OPT_ACCURACY)
/*
* This gives best accuracy but is not very fast.
*/
# define MAD_F_MLA(hi, lo, x, y) \
({ mad_fixed64hi_t __hi; \
mad_fixed64lo_t __lo; \
MAD_F_MLX(__hi, __lo, (x), (y)); \
asm ("addl %2,%0\n\t" \
"adcl %3,%1" \
: "=rm" (lo), "=rm" (hi) \
: "r" (__lo), "r" (__hi), "0" (lo), "1" (hi) \
: "cc"); \
})
# endif /* OPT_ACCURACY */
# if defined(OPT_ACCURACY)
/*
* Surprisingly, this is faster than SHRD followed by ADC.
*/
# define mad_f_scale64(hi, lo) \
({ mad_fixed64hi_t __hi_; \
mad_fixed64lo_t __lo_; \
mad_fixed_t __result; \
asm ("addl %4,%2\n\t" \
"adcl %5,%3" \
: "=rm" (__lo_), "=rm" (__hi_) \
: "0" (lo), "1" (hi), \
"ir" (1L << (MAD_F_SCALEBITS - 1)), "ir" (0) \
: "cc"); \
asm ("shrdl %3,%2,%1" \
: "=rm" (__result) \
: "0" (__lo_), "r" (__hi_), "I" (MAD_F_SCALEBITS) \
: "cc"); \
__result; \
})
# elif defined(OPT_INTEL)
/*
* Alternate Intel scaling that may or may not perform better.
*/
# define mad_f_scale64(hi, lo) \
({ mad_fixed_t __result; \
asm ("shrl %3,%1\n\t" \
"shll %4,%2\n\t" \
"orl %2,%1" \
: "=rm" (__result) \
: "0" (lo), "r" (hi), \
"I" (MAD_F_SCALEBITS), "I" (32 - MAD_F_SCALEBITS) \
: "cc"); \
__result; \
})
# else
# define mad_f_scale64(hi, lo) \
({ mad_fixed_t __result; \
asm ("shrdl %3,%2,%1" \
: "=rm" (__result) \
: "0" (lo), "r" (hi), "I" (MAD_F_SCALEBITS) \
: "cc"); \
__result; \
})
# endif /* OPT_ACCURACY */
# define MAD_F_SCALEBITS MAD_F_FRACBITS
# endif
/* --- ARM ----------------------------------------------------------------- */
# elif defined(FPM_ARM)
/*
* This ARM V4 version is as accurate as FPM_64BIT but much faster. The
* least significant bit is properly rounded at no CPU cycle cost!
*/
# if 1
/*
* This is faster than the default implementation via MAD_F_MLX() and
* mad_f_scale64().
*/
# define mad_f_mul(x, y) \
({ mad_fixed64hi_t __hi; \
mad_fixed64lo_t __lo; \
mad_fixed_t __result; \
asm ("smull %0, %1, %3, %4\n\t" \
"movs %0, %0, lsr %5\n\t" \
"adc %2, %0, %1, lsl %6" \
: "=&r" (__lo), "=&r" (__hi), "=r" (__result) \
: "%r" (x), "r" (y), \
"M" (MAD_F_SCALEBITS), "M" (32 - MAD_F_SCALEBITS) \
: "cc"); \
__result; \
})
# endif
# define MAD_F_MLX(hi, lo, x, y) \
asm ("smull %0, %1, %2, %3" \
: "=&r" (lo), "=&r" (hi) \
: "%r" (x), "r" (y))
# define MAD_F_MLA(hi, lo, x, y) \
asm ("smlal %0, %1, %2, %3" \
: "+r" (lo), "+r" (hi) \
: "%r" (x), "r" (y))
# define MAD_F_MLN(hi, lo) \
asm ("rsbs %0, %2, #0\n\t" \
"rsc %1, %3, #0" \
: "=r" (lo), "=r" (hi) \
: "0" (lo), "1" (hi) \
: "cc")
# define mad_f_scale64(hi, lo) \
({ mad_fixed_t __result; \
asm ("movs %0, %1, lsr %3\n\t" \
"adc %0, %0, %2, lsl %4" \
: "=&r" (__result) \
: "r" (lo), "r" (hi), \
"M" (MAD_F_SCALEBITS), "M" (32 - MAD_F_SCALEBITS) \
: "cc"); \
__result; \
})
# define MAD_F_SCALEBITS MAD_F_FRACBITS
/* --- MIPS ---------------------------------------------------------------- */
# elif defined(FPM_MIPS)
/*
* This MIPS version is fast and accurate; the disposition of the least
* significant bit depends on OPT_ACCURACY via mad_f_scale64().
*/
# define MAD_F_MLX(hi, lo, x, y) \
asm ("mult %2,%3" \
: "=l" (lo), "=h" (hi) \
: "%r" (x), "r" (y))
# if defined(HAVE_MADD_ASM)
# define MAD_F_MLA(hi, lo, x, y) \
asm ("madd %2,%3" \
: "+l" (lo), "+h" (hi) \
: "%r" (x), "r" (y))
# elif defined(HAVE_MADD16_ASM)
/*
* This loses significant accuracy due to the 16-bit integer limit in the
* multiply/accumulate instruction.
*/
# define MAD_F_ML0(hi, lo, x, y) \
asm ("mult %2,%3" \
: "=l" (lo), "=h" (hi) \
: "%r" ((x) >> 12), "r" ((y) >> 16))
# define MAD_F_MLA(hi, lo, x, y) \
asm ("madd16 %2,%3" \
: "+l" (lo), "+h" (hi) \
: "%r" ((x) >> 12), "r" ((y) >> 16))
# define MAD_F_MLZ(hi, lo) ((mad_fixed_t) (lo))
# endif
# if defined(OPT_SPEED)
# define mad_f_scale64(hi, lo) \
((mad_fixed_t) ((hi) << (32 - MAD_F_SCALEBITS)))
# define MAD_F_SCALEBITS MAD_F_FRACBITS
# endif
/* --- SPARC --------------------------------------------------------------- */
# elif defined(FPM_SPARC)
/*
* This SPARC V8 version is fast and accurate; the disposition of the least
* significant bit depends on OPT_ACCURACY via mad_f_scale64().
*/
# define MAD_F_MLX(hi, lo, x, y) \
asm ("smul %2, %3, %0\n\t" \
"rd %%y, %1" \
: "=r" (lo), "=r" (hi) \
: "%r" (x), "rI" (y))
/* --- PowerPC ------------------------------------------------------------- */
# elif defined(FPM_PPC)
/*
* This PowerPC version is fast and accurate; the disposition of the least
* significant bit depends on OPT_ACCURACY via mad_f_scale64().
*/
# define MAD_F_MLX(hi, lo, x, y) \
do { \
asm ("mullw %0,%1,%2" \
: "=r" (lo) \
: "%r" (x), "r" (y)); \
asm ("mulhw %0,%1,%2" \
: "=r" (hi) \
: "%r" (x), "r" (y)); \
} \
while (0)
# if defined(OPT_ACCURACY)
/*
* This gives best accuracy but is not very fast.
*/
# define MAD_F_MLA(hi, lo, x, y) \
({ mad_fixed64hi_t __hi; \
mad_fixed64lo_t __lo; \
MAD_F_MLX(__hi, __lo, (x), (y)); \
asm ("addc %0,%2,%3\n\t" \
"adde %1,%4,%5" \
: "=r" (lo), "=r" (hi) \
: "%r" (lo), "r" (__lo), \
"%r" (hi), "r" (__hi) \
: "xer"); \
})
# endif
# if defined(OPT_ACCURACY)
/*
* This is slower than the truncating version below it.
*/
# define mad_f_scale64(hi, lo) \
({ mad_fixed_t __result, __round; \
asm ("rotrwi %0,%1,%2" \
: "=r" (__result) \
: "r" (lo), "i" (MAD_F_SCALEBITS)); \
asm ("extrwi %0,%1,1,0" \
: "=r" (__round) \
: "r" (__result)); \
asm ("insrwi %0,%1,%2,0" \
: "+r" (__result) \
: "r" (hi), "i" (MAD_F_SCALEBITS)); \
asm ("add %0,%1,%2" \
: "=r" (__result) \
: "%r" (__result), "r" (__round)); \
__result; \
})
# else
# define mad_f_scale64(hi, lo) \
({ mad_fixed_t __result; \
asm ("rotrwi %0,%1,%2" \
: "=r" (__result) \
: "r" (lo), "i" (MAD_F_SCALEBITS)); \
asm ("insrwi %0,%1,%2,0" \
: "+r" (__result) \
: "r" (hi), "i" (MAD_F_SCALEBITS)); \
__result; \
})
# endif
# define MAD_F_SCALEBITS MAD_F_FRACBITS
/* --- Default ------------------------------------------------------------- */
# elif defined(FPM_DEFAULT)
/*
* This version is the most portable but it loses significant accuracy.
* Furthermore, accuracy is biased against the second argument, so care
* should be taken when ordering operands.
*
* The scale factors are constant as this is not used with SSO.
*
* Pre-rounding is required to stay within the limits of compliance.
*/
# if defined(OPT_SPEED)
# define mad_f_mul(x, y) (((x) >> 12) * ((y) >> 16))
# else
# define mad_f_mul(x, y) ((((x) + (1L << 11)) >> 12) * \
(((y) + (1L << 15)) >> 16))
# endif
/* ------------------------------------------------------------------------- */
# else
# error "no FPM selected"
# endif
/* default implementations */
# if !defined(mad_f_mul)
# define mad_f_mul(x, y) \
({ register mad_fixed64hi_t __hi; \
register mad_fixed64lo_t __lo; \
MAD_F_MLX(__hi, __lo, (x), (y)); \
mad_f_scale64(__hi, __lo); \
})
# endif
# if !defined(MAD_F_MLA)
# define MAD_F_ML0(hi, lo, x, y) ((lo) = mad_f_mul((x), (y)))
# define MAD_F_MLA(hi, lo, x, y) ((lo) += mad_f_mul((x), (y)))
# define MAD_F_MLN(hi, lo) ((lo) = -(lo))
# define MAD_F_MLZ(hi, lo) ((void) (hi), (mad_fixed_t) (lo))
# endif
# if !defined(MAD_F_ML0)
# define MAD_F_ML0(hi, lo, x, y) MAD_F_MLX((hi), (lo), (x), (y))
# endif
# if !defined(MAD_F_MLN)
# define MAD_F_MLN(hi, lo) ((hi) = ((lo) = -(lo)) ? ~(hi) : -(hi))
# endif
# if !defined(MAD_F_MLZ)
# define MAD_F_MLZ(hi, lo) mad_f_scale64((hi), (lo))
# endif
# if !defined(mad_f_scale64)
# if defined(OPT_ACCURACY)
# define mad_f_scale64(hi, lo) \
((((mad_fixed_t) \
(((hi) << (32 - (MAD_F_SCALEBITS - 1))) | \
((lo) >> (MAD_F_SCALEBITS - 1)))) + 1) >> 1)
# else
# define mad_f_scale64(hi, lo) \
((mad_fixed_t) \
(((hi) << (32 - MAD_F_SCALEBITS)) | \
((lo) >> MAD_F_SCALEBITS)))
# endif
# define MAD_F_SCALEBITS MAD_F_FRACBITS
# endif
/* C routines */
mad_fixed_t mad_f_abs(mad_fixed_t);
mad_fixed_t mad_f_div(mad_fixed_t, mad_fixed_t);
# endif
/* Id: bit.h,v 1.12 2004/01/23 09:41:32 rob Exp */
# ifndef LIBMAD_BIT_H
# define LIBMAD_BIT_H
struct mad_bitptr {
unsigned char const *byte;
unsigned short cache;
unsigned short left;
};
void mad_bit_init(struct mad_bitptr *, unsigned char const *);
# define mad_bit_finish(bitptr) /* nothing */
unsigned int mad_bit_length(struct mad_bitptr const *,
struct mad_bitptr const *);
# define mad_bit_bitsleft(bitptr) ((bitptr)->left)
unsigned char const *mad_bit_nextbyte(struct mad_bitptr const *);
void mad_bit_skip(struct mad_bitptr *, unsigned int);
unsigned long mad_bit_read(struct mad_bitptr *, unsigned int);
void mad_bit_write(struct mad_bitptr *, unsigned int, unsigned long);
unsigned short mad_bit_crc(struct mad_bitptr, unsigned int, unsigned short);
# endif
/* Id: timer.h,v 1.16 2004/01/23 09:41:33 rob Exp */
# ifndef LIBMAD_TIMER_H
# define LIBMAD_TIMER_H
typedef struct {
signed long seconds; /* whole seconds */
unsigned long fraction; /* 1/MAD_TIMER_RESOLUTION seconds */
} mad_timer_t;
extern mad_timer_t const mad_timer_zero;
# define MAD_TIMER_RESOLUTION 352800000UL
enum mad_units {
MAD_UNITS_HOURS = -2,
MAD_UNITS_MINUTES = -1,
MAD_UNITS_SECONDS = 0,
/* metric units */
MAD_UNITS_DECISECONDS = 10,
MAD_UNITS_CENTISECONDS = 100,
MAD_UNITS_MILLISECONDS = 1000,
/* audio sample units */
MAD_UNITS_8000_HZ = 8000,
MAD_UNITS_11025_HZ = 11025,
MAD_UNITS_12000_HZ = 12000,
MAD_UNITS_16000_HZ = 16000,
MAD_UNITS_22050_HZ = 22050,
MAD_UNITS_24000_HZ = 24000,
MAD_UNITS_32000_HZ = 32000,
MAD_UNITS_44100_HZ = 44100,
MAD_UNITS_48000_HZ = 48000,
/* video frame/field units */
MAD_UNITS_24_FPS = 24,
MAD_UNITS_25_FPS = 25,
MAD_UNITS_30_FPS = 30,
MAD_UNITS_48_FPS = 48,
MAD_UNITS_50_FPS = 50,
MAD_UNITS_60_FPS = 60,
/* CD audio frames */
MAD_UNITS_75_FPS = 75,
/* video drop-frame units */
MAD_UNITS_23_976_FPS = -24,
MAD_UNITS_24_975_FPS = -25,
MAD_UNITS_29_97_FPS = -30,
MAD_UNITS_47_952_FPS = -48,
MAD_UNITS_49_95_FPS = -50,
MAD_UNITS_59_94_FPS = -60
};
# define mad_timer_reset(timer) ((void) (*(timer) = mad_timer_zero))
int mad_timer_compare(mad_timer_t, mad_timer_t);
# define mad_timer_sign(timer) mad_timer_compare((timer), mad_timer_zero)
void mad_timer_negate(mad_timer_t *);
mad_timer_t mad_timer_abs(mad_timer_t);
void mad_timer_set(mad_timer_t *, unsigned long, unsigned long, unsigned long);
void mad_timer_add(mad_timer_t *, mad_timer_t);
void mad_timer_multiply(mad_timer_t *, signed long);
signed long mad_timer_count(mad_timer_t, enum mad_units);
unsigned long mad_timer_fraction(mad_timer_t, unsigned long);
void mad_timer_string(mad_timer_t, char *, char const *,
enum mad_units, enum mad_units, unsigned long);
# endif
/* Id: stream.h,v 1.20 2004/02/05 09:02:39 rob Exp */
# ifndef LIBMAD_STREAM_H
# define LIBMAD_STREAM_H
# define MAD_BUFFER_GUARD 8
# define MAD_BUFFER_MDLEN (511 + 2048 + MAD_BUFFER_GUARD)
enum mad_error {
MAD_ERROR_NONE = 0x0000, /* no error */
MAD_ERROR_BUFLEN = 0x0001, /* input buffer too small (or EOF) */
MAD_ERROR_BUFPTR = 0x0002, /* invalid (null) buffer pointer */
MAD_ERROR_NOMEM = 0x0031, /* not enough memory */
MAD_ERROR_LOSTSYNC = 0x0101, /* lost synchronization */
MAD_ERROR_BADLAYER = 0x0102, /* reserved header layer value */
MAD_ERROR_BADBITRATE = 0x0103, /* forbidden bitrate value */
MAD_ERROR_BADSAMPLERATE = 0x0104, /* reserved sample frequency value */
MAD_ERROR_BADEMPHASIS = 0x0105, /* reserved emphasis value */
MAD_ERROR_BADCRC = 0x0201, /* CRC check failed */
MAD_ERROR_BADBITALLOC = 0x0211, /* forbidden bit allocation value */
MAD_ERROR_BADSCALEFACTOR = 0x0221, /* bad scalefactor index */
MAD_ERROR_BADMODE = 0x0222, /* bad bitrate/mode combination */
MAD_ERROR_BADFRAMELEN = 0x0231, /* bad frame length */
MAD_ERROR_BADBIGVALUES = 0x0232, /* bad big_values count */
MAD_ERROR_BADBLOCKTYPE = 0x0233, /* reserved block_type */
MAD_ERROR_BADSCFSI = 0x0234, /* bad scalefactor selection info */
MAD_ERROR_BADDATAPTR = 0x0235, /* bad main_data_begin pointer */
MAD_ERROR_BADPART3LEN = 0x0236, /* bad audio data length */
MAD_ERROR_BADHUFFTABLE = 0x0237, /* bad Huffman table select */
MAD_ERROR_BADHUFFDATA = 0x0238, /* Huffman data overrun */
MAD_ERROR_BADSTEREO = 0x0239 /* incompatible block_type for JS */
};
# define MAD_RECOVERABLE(error) ((error) & 0xff00)
struct mad_stream {
unsigned char const *buffer; /* input bitstream buffer */
unsigned char const *bufend; /* end of buffer */
unsigned long skiplen; /* bytes to skip before next frame */
int sync; /* stream sync found */
unsigned long freerate; /* free bitrate (fixed) */
unsigned char const *this_frame; /* start of current frame */
unsigned char const *next_frame; /* start of next frame */
struct mad_bitptr ptr; /* current processing bit pointer */
struct mad_bitptr anc_ptr; /* ancillary bits pointer */
unsigned int anc_bitlen; /* number of ancillary bits */
unsigned char (*main_data)[MAD_BUFFER_MDLEN];
/* Layer III main_data() */
unsigned int md_len; /* bytes in main_data */
int options; /* decoding options (see below) */
enum mad_error error; /* error code (see above) */
};
enum {
MAD_OPTION_IGNORECRC = 0x0001, /* ignore CRC errors */
MAD_OPTION_HALFSAMPLERATE = 0x0002 /* generate PCM at 1/2 sample rate */
# if 0 /* not yet implemented */
MAD_OPTION_LEFTCHANNEL = 0x0010, /* decode left channel only */
MAD_OPTION_RIGHTCHANNEL = 0x0020, /* decode right channel only */
MAD_OPTION_SINGLECHANNEL = 0x0030 /* combine channels */
# endif
};
void mad_stream_init(struct mad_stream *);
void mad_stream_finish(struct mad_stream *);
# define mad_stream_options(stream, opts) \
((void) ((stream)->options = (opts)))
void mad_stream_buffer(struct mad_stream *,
unsigned char const *, unsigned long);
void mad_stream_skip(struct mad_stream *, unsigned long);
int mad_stream_sync(struct mad_stream *);
char const *mad_stream_errorstr(struct mad_stream const *);
# endif
/* Id: frame.h,v 1.20 2004/01/23 09:41:32 rob Exp */
# ifndef LIBMAD_FRAME_H
# define LIBMAD_FRAME_H
enum mad_layer {
MAD_LAYER_I = 1, /* Layer I */
MAD_LAYER_II = 2, /* Layer II */
MAD_LAYER_III = 3 /* Layer III */
};
enum mad_mode {
MAD_MODE_SINGLE_CHANNEL = 0, /* single channel */
MAD_MODE_DUAL_CHANNEL = 1, /* dual channel */
MAD_MODE_JOINT_STEREO = 2, /* joint (MS/intensity) stereo */
MAD_MODE_STEREO = 3 /* normal LR stereo */
};
enum mad_emphasis {
MAD_EMPHASIS_NONE = 0, /* no emphasis */
MAD_EMPHASIS_50_15_US = 1, /* 50/15 microseconds emphasis */
MAD_EMPHASIS_CCITT_J_17 = 3, /* CCITT J.17 emphasis */
MAD_EMPHASIS_RESERVED = 2 /* unknown emphasis */
};
struct mad_header {
enum mad_layer layer; /* audio layer (1, 2, or 3) */
enum mad_mode mode; /* channel mode (see above) */
int mode_extension; /* additional mode info */
enum mad_emphasis emphasis; /* de-emphasis to use (see above) */
unsigned long bitrate; /* stream bitrate (bps) */
unsigned int samplerate; /* sampling frequency (Hz) */
unsigned short crc_check; /* frame CRC accumulator */
unsigned short crc_target; /* final target CRC checksum */
int flags; /* flags (see below) */
int private_bits; /* private bits (see below) */
mad_timer_t duration; /* audio playing time of frame */
};
struct mad_frame {
struct mad_header header; /* MPEG audio header */
int options; /* decoding options (from stream) */
mad_fixed_t sbsample[2][36][32]; /* synthesis subband filter samples */
mad_fixed_t (*overlap)[2][32][18]; /* Layer III block overlap data */
};
# define MAD_NCHANNELS(header) ((header)->mode ? 2 : 1)
# define MAD_NSBSAMPLES(header) \
((header)->layer == MAD_LAYER_I ? 12 : \
(((header)->layer == MAD_LAYER_III && \
((header)->flags & MAD_FLAG_LSF_EXT)) ? 18 : 36))
enum {
MAD_FLAG_NPRIVATE_III = 0x0007, /* number of Layer III private bits */
MAD_FLAG_INCOMPLETE = 0x0008, /* header but not data is decoded */
MAD_FLAG_PROTECTION = 0x0010, /* frame has CRC protection */
MAD_FLAG_COPYRIGHT = 0x0020, /* frame is copyright */
MAD_FLAG_ORIGINAL = 0x0040, /* frame is original (else copy) */
MAD_FLAG_PADDING = 0x0080, /* frame has additional slot */
MAD_FLAG_I_STEREO = 0x0100, /* uses intensity joint stereo */
MAD_FLAG_MS_STEREO = 0x0200, /* uses middle/side joint stereo */
MAD_FLAG_FREEFORMAT = 0x0400, /* uses free format bitrate */
MAD_FLAG_LSF_EXT = 0x1000, /* lower sampling freq. extension */
MAD_FLAG_MC_EXT = 0x2000, /* multichannel audio extension */
MAD_FLAG_MPEG_2_5_EXT = 0x4000 /* MPEG 2.5 (unofficial) extension */
};
enum {
MAD_PRIVATE_HEADER = 0x0100, /* header private bit */
MAD_PRIVATE_III = 0x001f /* Layer III private bits (up to 5) */
};
void mad_header_init(struct mad_header *);
# define mad_header_finish(header) /* nothing */
int mad_header_decode(struct mad_header *, struct mad_stream *);
void mad_frame_init(struct mad_frame *);
void mad_frame_finish(struct mad_frame *);
int mad_frame_decode(struct mad_frame *, struct mad_stream *);
void mad_frame_mute(struct mad_frame *);
# endif
/* Id: synth.h,v 1.15 2004/01/23 09:41:33 rob Exp */
# ifndef LIBMAD_SYNTH_H
# define LIBMAD_SYNTH_H
struct mad_pcm {
unsigned int samplerate; /* sampling frequency (Hz) */
unsigned short channels; /* number of channels */
unsigned short length; /* number of samples per channel */
mad_fixed_t samples[2][1152]; /* PCM output samples [ch][sample] */
};
struct mad_synth {
mad_fixed_t filter[2][2][2][16][8]; /* polyphase filterbank outputs */
/* [ch][eo][peo][s][v] */
unsigned int phase; /* current processing phase */
struct mad_pcm pcm; /* PCM output */
};
/* single channel PCM selector */
enum {
MAD_PCM_CHANNEL_SINGLE = 0
};
/* dual channel PCM selector */
enum {
MAD_PCM_CHANNEL_DUAL_1 = 0,
MAD_PCM_CHANNEL_DUAL_2 = 1
};
/* stereo PCM selector */
enum {
MAD_PCM_CHANNEL_STEREO_LEFT = 0,
MAD_PCM_CHANNEL_STEREO_RIGHT = 1
};
void mad_synth_init(struct mad_synth *);
# define mad_synth_finish(synth) /* nothing */
void mad_synth_mute(struct mad_synth *);
void mad_synth_frame(struct mad_synth *, struct mad_frame const *);
# endif
/* Id: decoder.h,v 1.17 2004/01/23 09:41:32 rob Exp */
# ifndef LIBMAD_DECODER_H
# define LIBMAD_DECODER_H
enum mad_decoder_mode {
MAD_DECODER_MODE_SYNC = 0,
MAD_DECODER_MODE_ASYNC
};
enum mad_flow {
MAD_FLOW_CONTINUE = 0x0000, /* continue normally */
MAD_FLOW_STOP = 0x0010, /* stop decoding normally */
MAD_FLOW_BREAK = 0x0011, /* stop decoding and signal an error */
MAD_FLOW_IGNORE = 0x0020 /* ignore the current frame */
};
struct mad_decoder {
enum mad_decoder_mode mode;
int options;
struct {
long pid;
int in;
int out;
} async;
struct {
struct mad_stream stream;
struct mad_frame frame;
struct mad_synth synth;
} *sync;
void *cb_data;
enum mad_flow (*input_func)(void *, struct mad_stream *);
enum mad_flow (*header_func)(void *, struct mad_header const *);
enum mad_flow (*filter_func)(void *,
struct mad_stream const *, struct mad_frame *);
enum mad_flow (*output_func)(void *,
struct mad_header const *, struct mad_pcm *);
enum mad_flow (*error_func)(void *, struct mad_stream *, struct mad_frame *);
enum mad_flow (*message_func)(void *, void *, unsigned int *);
};
void mad_decoder_init(struct mad_decoder *, void *,
enum mad_flow (*)(void *, struct mad_stream *),
enum mad_flow (*)(void *, struct mad_header const *),
enum mad_flow (*)(void *,
struct mad_stream const *,
struct mad_frame *),
enum mad_flow (*)(void *,
struct mad_header const *,
struct mad_pcm *),
enum mad_flow (*)(void *,
struct mad_stream *,
struct mad_frame *),
enum mad_flow (*)(void *, void *, unsigned int *));
int mad_decoder_finish(struct mad_decoder *);
# define mad_decoder_options(decoder, opts) \
((void) ((decoder)->options = (opts)))
int mad_decoder_run(struct mad_decoder *, enum mad_decoder_mode);
int mad_decoder_message(struct mad_decoder *, void *, unsigned int *);
# endif
# ifdef __cplusplus
}
# endif

Binary file not shown.

Binary file not shown.