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:
parent
31281197d4
commit
7ff653b48f
14 changed files with 3714 additions and 1964 deletions
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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
|
369
Plugins/minimp3/MP3Decoder.m
Normal file
369
Plugins/minimp3/MP3Decoder.m
Normal 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
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
1397
Plugins/minimp3/ThirdParty/minimp3_ex.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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 */,
|
12
ThirdParty/libmad/README.md
vendored
12
ThirdParty/libmad/README.md
vendored
|
@ -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.
|
964
ThirdParty/libmad/include/MAD/mad.h
vendored
964
ThirdParty/libmad/include/MAD/mad.h
vendored
|
@ -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
|
BIN
ThirdParty/libraries-debug-overlay.tar.xz
vendored
BIN
ThirdParty/libraries-debug-overlay.tar.xz
vendored
Binary file not shown.
BIN
ThirdParty/libraries.tar.xz
vendored
BIN
ThirdParty/libraries.tar.xz
vendored
Binary file not shown.
Loading…
Reference in a new issue