Updated VGMStream to r1050-925-g2b92a562, now with Atrac9 support.
parent
6c1c3f3615
commit
c6c325a303
|
@ -0,0 +1,441 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 48;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
830EBDF020045FF80023AA10 /* tables.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDB920045FF80023AA10 /* tables.c */; };
|
||||
830EBDF120045FF80023AA10 /* libatrac9.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDBA20045FF80023AA10 /* libatrac9.c */; };
|
||||
830EBDF220045FF80023AA10 /* huffCodes.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDBB20045FF80023AA10 /* huffCodes.c */; };
|
||||
830EBDF320045FF80023AA10 /* imdct.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDBC20045FF80023AA10 /* imdct.c */; };
|
||||
830EBDF420045FF80023AA10 /* scale_factors.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDBD20045FF80023AA10 /* scale_factors.h */; };
|
||||
830EBDF520045FF80023AA10 /* utility.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDBE20045FF80023AA10 /* utility.c */; };
|
||||
830EBDF620045FF80023AA10 /* quantization.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDBF20045FF80023AA10 /* quantization.h */; };
|
||||
830EBDF720045FF80023AA10 /* band_extension.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDC020045FF80023AA10 /* band_extension.c */; };
|
||||
830EBDF820045FF80023AA10 /* unpack.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDC120045FF80023AA10 /* unpack.c */; };
|
||||
830EBDF920045FF80023AA10 /* bit_reader.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDC220045FF80023AA10 /* bit_reader.h */; };
|
||||
830EBDFA20045FF80023AA10 /* decoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDC320045FF80023AA10 /* decoder.h */; };
|
||||
830EBDFB20045FF80023AA10 /* decinit.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDC420045FF80023AA10 /* decinit.c */; };
|
||||
830EBDFC20045FF80023AA10 /* bit_allocation.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDC520045FF80023AA10 /* bit_allocation.h */; };
|
||||
830EBDFE20045FF80023AA10 /* libatrac9.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDC720045FF80023AA10 /* libatrac9.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
830EBDFF20045FF80023AA10 /* tables.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDC820045FF80023AA10 /* tables.h */; };
|
||||
830EBE0020045FF80023AA10 /* scale_factors.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDC920045FF80023AA10 /* scale_factors.c */; };
|
||||
830EBE0120045FF80023AA10 /* error_codes.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDCA20045FF80023AA10 /* error_codes.h */; };
|
||||
830EBE0220045FF80023AA10 /* imdct.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDCB20045FF80023AA10 /* imdct.h */; };
|
||||
830EBE0420045FF80023AA10 /* huffCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDCD20045FF80023AA10 /* huffCodes.h */; };
|
||||
830EBE0520045FF80023AA10 /* decinit.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDCE20045FF80023AA10 /* decinit.h */; };
|
||||
830EBE0620045FF80023AA10 /* decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDCF20045FF80023AA10 /* decoder.c */; };
|
||||
830EBE0720045FF80023AA10 /* bit_reader.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDD020045FF80023AA10 /* bit_reader.c */; };
|
||||
830EBE0820045FF80023AA10 /* unpack.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDD120045FF80023AA10 /* unpack.h */; };
|
||||
830EBE0A20045FF80023AA10 /* quantization.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDD320045FF80023AA10 /* quantization.c */; };
|
||||
830EBE0B20045FF80023AA10 /* utility.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDD420045FF80023AA10 /* utility.h */; };
|
||||
830EBE0C20045FF80023AA10 /* band_extension.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDD520045FF80023AA10 /* band_extension.h */; };
|
||||
830EBE0D20045FF80023AA10 /* bit_allocation.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBDD620045FF80023AA10 /* bit_allocation.c */; };
|
||||
830EBE0E20045FF80023AA10 /* structures.h in Headers */ = {isa = PBXBuildFile; fileRef = 830EBDD720045FF80023AA10 /* structures.h */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
830EBD8720045F190023AA10 /* libatrac9.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = libatrac9.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
830EBD8B20045F190023AA10 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
830EBDB920045FF80023AA10 /* tables.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tables.c; sourceTree = "<group>"; };
|
||||
830EBDBA20045FF80023AA10 /* libatrac9.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = libatrac9.c; sourceTree = "<group>"; };
|
||||
830EBDBB20045FF80023AA10 /* huffCodes.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = huffCodes.c; sourceTree = "<group>"; };
|
||||
830EBDBC20045FF80023AA10 /* imdct.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = imdct.c; sourceTree = "<group>"; };
|
||||
830EBDBD20045FF80023AA10 /* scale_factors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scale_factors.h; sourceTree = "<group>"; };
|
||||
830EBDBE20045FF80023AA10 /* utility.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = utility.c; sourceTree = "<group>"; };
|
||||
830EBDBF20045FF80023AA10 /* quantization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = quantization.h; sourceTree = "<group>"; };
|
||||
830EBDC020045FF80023AA10 /* band_extension.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = band_extension.c; sourceTree = "<group>"; };
|
||||
830EBDC120045FF80023AA10 /* unpack.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = unpack.c; sourceTree = "<group>"; };
|
||||
830EBDC220045FF80023AA10 /* bit_reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bit_reader.h; sourceTree = "<group>"; };
|
||||
830EBDC320045FF80023AA10 /* decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = decoder.h; sourceTree = "<group>"; };
|
||||
830EBDC420045FF80023AA10 /* decinit.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = decinit.c; sourceTree = "<group>"; };
|
||||
830EBDC520045FF80023AA10 /* bit_allocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bit_allocation.h; sourceTree = "<group>"; };
|
||||
830EBDC720045FF80023AA10 /* libatrac9.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libatrac9.h; sourceTree = "<group>"; };
|
||||
830EBDC820045FF80023AA10 /* tables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tables.h; sourceTree = "<group>"; };
|
||||
830EBDC920045FF80023AA10 /* scale_factors.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = scale_factors.c; sourceTree = "<group>"; };
|
||||
830EBDCA20045FF80023AA10 /* error_codes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = error_codes.h; sourceTree = "<group>"; };
|
||||
830EBDCB20045FF80023AA10 /* imdct.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = imdct.h; sourceTree = "<group>"; };
|
||||
830EBDCD20045FF80023AA10 /* huffCodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = huffCodes.h; sourceTree = "<group>"; };
|
||||
830EBDCE20045FF80023AA10 /* decinit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = decinit.h; sourceTree = "<group>"; };
|
||||
830EBDCF20045FF80023AA10 /* decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = decoder.c; sourceTree = "<group>"; };
|
||||
830EBDD020045FF80023AA10 /* bit_reader.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bit_reader.c; sourceTree = "<group>"; };
|
||||
830EBDD120045FF80023AA10 /* unpack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = unpack.h; sourceTree = "<group>"; };
|
||||
830EBDD320045FF80023AA10 /* quantization.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = quantization.c; sourceTree = "<group>"; };
|
||||
830EBDD420045FF80023AA10 /* utility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utility.h; sourceTree = "<group>"; };
|
||||
830EBDD520045FF80023AA10 /* band_extension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = band_extension.h; sourceTree = "<group>"; };
|
||||
830EBDD620045FF80023AA10 /* bit_allocation.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bit_allocation.c; sourceTree = "<group>"; };
|
||||
830EBDD720045FF80023AA10 /* structures.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = structures.h; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
830EBD8320045F190023AA10 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
830EBD7D20045F190023AA10 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
830EBD8920045F190023AA10 /* libatrac9 */,
|
||||
830EBD8820045F190023AA10 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
830EBD8820045F190023AA10 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
830EBD8720045F190023AA10 /* libatrac9.framework */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
830EBD8920045F190023AA10 /* libatrac9 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
830EBD9C20045FF80023AA10 /* LibAtrac9 */,
|
||||
830EBD8B20045F190023AA10 /* Info.plist */,
|
||||
);
|
||||
path = libatrac9;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
830EBD9C20045FF80023AA10 /* LibAtrac9 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
830EBDB820045FF80023AA10 /* C */,
|
||||
);
|
||||
path = LibAtrac9;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
830EBDB820045FF80023AA10 /* C */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
830EBDC020045FF80023AA10 /* band_extension.c */,
|
||||
830EBDD520045FF80023AA10 /* band_extension.h */,
|
||||
830EBDD620045FF80023AA10 /* bit_allocation.c */,
|
||||
830EBDC520045FF80023AA10 /* bit_allocation.h */,
|
||||
830EBDD020045FF80023AA10 /* bit_reader.c */,
|
||||
830EBDC220045FF80023AA10 /* bit_reader.h */,
|
||||
830EBDC420045FF80023AA10 /* decinit.c */,
|
||||
830EBDCE20045FF80023AA10 /* decinit.h */,
|
||||
830EBDCF20045FF80023AA10 /* decoder.c */,
|
||||
830EBDC320045FF80023AA10 /* decoder.h */,
|
||||
830EBDCA20045FF80023AA10 /* error_codes.h */,
|
||||
830EBDBB20045FF80023AA10 /* huffCodes.c */,
|
||||
830EBDCD20045FF80023AA10 /* huffCodes.h */,
|
||||
830EBDBC20045FF80023AA10 /* imdct.c */,
|
||||
830EBDCB20045FF80023AA10 /* imdct.h */,
|
||||
830EBDBA20045FF80023AA10 /* libatrac9.c */,
|
||||
830EBDC720045FF80023AA10 /* libatrac9.h */,
|
||||
830EBDD320045FF80023AA10 /* quantization.c */,
|
||||
830EBDBF20045FF80023AA10 /* quantization.h */,
|
||||
830EBDC920045FF80023AA10 /* scale_factors.c */,
|
||||
830EBDBD20045FF80023AA10 /* scale_factors.h */,
|
||||
830EBDD720045FF80023AA10 /* structures.h */,
|
||||
830EBDB920045FF80023AA10 /* tables.c */,
|
||||
830EBDC820045FF80023AA10 /* tables.h */,
|
||||
830EBDC120045FF80023AA10 /* unpack.c */,
|
||||
830EBDD120045FF80023AA10 /* unpack.h */,
|
||||
830EBDBE20045FF80023AA10 /* utility.c */,
|
||||
830EBDD420045FF80023AA10 /* utility.h */,
|
||||
);
|
||||
path = C;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
830EBD8420045F190023AA10 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
830EBDFE20045FF80023AA10 /* libatrac9.h in Headers */,
|
||||
830EBE0220045FF80023AA10 /* imdct.h in Headers */,
|
||||
830EBE0520045FF80023AA10 /* decinit.h in Headers */,
|
||||
830EBE0420045FF80023AA10 /* huffCodes.h in Headers */,
|
||||
830EBE0B20045FF80023AA10 /* utility.h in Headers */,
|
||||
830EBE0820045FF80023AA10 /* unpack.h in Headers */,
|
||||
830EBDF420045FF80023AA10 /* scale_factors.h in Headers */,
|
||||
830EBE0C20045FF80023AA10 /* band_extension.h in Headers */,
|
||||
830EBE0120045FF80023AA10 /* error_codes.h in Headers */,
|
||||
830EBDFA20045FF80023AA10 /* decoder.h in Headers */,
|
||||
830EBDFF20045FF80023AA10 /* tables.h in Headers */,
|
||||
830EBDF920045FF80023AA10 /* bit_reader.h in Headers */,
|
||||
830EBE0E20045FF80023AA10 /* structures.h in Headers */,
|
||||
830EBDFC20045FF80023AA10 /* bit_allocation.h in Headers */,
|
||||
830EBDF620045FF80023AA10 /* quantization.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
830EBD8620045F190023AA10 /* libatrac9 */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 830EBD8F20045F190023AA10 /* Build configuration list for PBXNativeTarget "libatrac9" */;
|
||||
buildPhases = (
|
||||
830EBD8220045F190023AA10 /* Sources */,
|
||||
830EBD8320045F190023AA10 /* Frameworks */,
|
||||
830EBD8420045F190023AA10 /* Headers */,
|
||||
830EBD8520045F190023AA10 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = libatrac9;
|
||||
productName = libatrac9;
|
||||
productReference = 830EBD8720045F190023AA10 /* libatrac9.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
830EBD7E20045F190023AA10 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0920;
|
||||
ORGANIZATIONNAME = "Christopher Snowhill";
|
||||
TargetAttributes = {
|
||||
830EBD8620045F190023AA10 = {
|
||||
CreatedOnToolsVersion = 9.2;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 830EBD8120045F190023AA10 /* Build configuration list for PBXProject "libatrac9" */;
|
||||
compatibilityVersion = "Xcode 8.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = 830EBD7D20045F190023AA10;
|
||||
productRefGroup = 830EBD8820045F190023AA10 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
830EBD8620045F190023AA10 /* libatrac9 */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
830EBD8520045F190023AA10 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
830EBD8220045F190023AA10 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
830EBE0620045FF80023AA10 /* decoder.c in Sources */,
|
||||
830EBDF720045FF80023AA10 /* band_extension.c in Sources */,
|
||||
830EBE0020045FF80023AA10 /* scale_factors.c in Sources */,
|
||||
830EBDF520045FF80023AA10 /* utility.c in Sources */,
|
||||
830EBDFB20045FF80023AA10 /* decinit.c in Sources */,
|
||||
830EBE0720045FF80023AA10 /* bit_reader.c in Sources */,
|
||||
830EBE0A20045FF80023AA10 /* quantization.c in Sources */,
|
||||
830EBDF820045FF80023AA10 /* unpack.c in Sources */,
|
||||
830EBDF020045FF80023AA10 /* tables.c in Sources */,
|
||||
830EBDF320045FF80023AA10 /* imdct.c in Sources */,
|
||||
830EBDF220045FF80023AA10 /* huffCodes.c in Sources */,
|
||||
830EBDF120045FF80023AA10 /* libatrac9.c in Sources */,
|
||||
830EBE0D20045FF80023AA10 /* bit_allocation.c in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
830EBD8D20045F190023AA10 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
INSTALL_PATH = "@loader_path/Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
830EBD8E20045F190023AA10 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
INSTALL_PATH = "@loader_path/Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = macosx;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
830EBD9020045F190023AA10 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = N6E749HJ2X;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
FRAMEWORK_VERSION = A;
|
||||
HEADER_SEARCH_PATHS = libatrac9/LibAtrac9/C;
|
||||
INFOPLIST_FILE = libatrac9/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.7;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.kode54.libatrac9;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
830EBD9120045F190023AA10 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = N6E749HJ2X;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
FRAMEWORK_VERSION = A;
|
||||
HEADER_SEARCH_PATHS = libatrac9/LibAtrac9/C;
|
||||
INFOPLIST_FILE = libatrac9/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.7;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.kode54.libatrac9;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
830EBD8120045F190023AA10 /* Build configuration list for PBXProject "libatrac9" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
830EBD8D20045F190023AA10 /* Debug */,
|
||||
830EBD8E20045F190023AA10 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
830EBD8F20045F190023AA10 /* Build configuration list for PBXNativeTarget "libatrac9" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
830EBD9020045F190023AA10 /* Debug */,
|
||||
830EBD9120045F190023AA10 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 830EBD7E20045F190023AA10 /* Project object */;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2018 Christopher Snowhill. All rights reserved.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,63 @@
|
|||
###############################################################################
|
||||
# Set default behavior to automatically normalize line endings.
|
||||
###############################################################################
|
||||
* text=auto
|
||||
|
||||
###############################################################################
|
||||
# Set default behavior for command prompt diff.
|
||||
#
|
||||
# This is need for earlier builds of msysgit that does not have it on by
|
||||
# default for csharp files.
|
||||
# Note: This is only used by command line
|
||||
###############################################################################
|
||||
#*.cs diff=csharp
|
||||
|
||||
###############################################################################
|
||||
# Set the merge driver for project and solution files
|
||||
#
|
||||
# Merging from the command prompt will add diff markers to the files if there
|
||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||
# the diff markers are never inserted). Diff markers may cause the following
|
||||
# file extensions to fail to load in VS. An alternative would be to treat
|
||||
# these files as binary and thus will always conflict and require user
|
||||
# intervention with every merge. To do so, just uncomment the entries below
|
||||
###############################################################################
|
||||
#*.sln merge=binary
|
||||
#*.csproj merge=binary
|
||||
#*.vbproj merge=binary
|
||||
#*.vcxproj merge=binary
|
||||
#*.vcproj merge=binary
|
||||
#*.dbproj merge=binary
|
||||
#*.fsproj merge=binary
|
||||
#*.lsproj merge=binary
|
||||
#*.wixproj merge=binary
|
||||
#*.modelproj merge=binary
|
||||
#*.sqlproj merge=binary
|
||||
#*.wwaproj merge=binary
|
||||
|
||||
###############################################################################
|
||||
# behavior for image files
|
||||
#
|
||||
# image files are treated as binary by default.
|
||||
###############################################################################
|
||||
#*.jpg binary
|
||||
#*.png binary
|
||||
#*.gif binary
|
||||
|
||||
###############################################################################
|
||||
# diff behavior for common document formats
|
||||
#
|
||||
# Convert binary document formats to text before diffing them. This feature
|
||||
# is only available from the command line. Turn it on by uncommenting the
|
||||
# entries below.
|
||||
###############################################################################
|
||||
#*.doc diff=astextplain
|
||||
#*.DOC diff=astextplain
|
||||
#*.docx diff=astextplain
|
||||
#*.DOCX diff=astextplain
|
||||
#*.dot diff=astextplain
|
||||
#*.DOT diff=astextplain
|
||||
#*.pdf diff=astextplain
|
||||
#*.PDF diff=astextplain
|
||||
#*.rtf diff=astextplain
|
||||
#*.RTF diff=astextplain
|
|
@ -0,0 +1,261 @@
|
|||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# DNX
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
#*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/packages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/packages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/packages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignoreable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
node_modules/
|
||||
orleans.codegen.cs
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
# CodeRush
|
||||
.cr/
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
|
@ -0,0 +1,191 @@
|
|||
#include "band_extension.h"
|
||||
#include "tables.h"
|
||||
#include "utility.h"
|
||||
#include <math.h>
|
||||
|
||||
void ApplyBandExtension(block* block)
|
||||
{
|
||||
if (!block->BandExtensionEnabled || !block->HasExtensionData) return;
|
||||
|
||||
for (int i = 0; i < block->ChannelCount; i++)
|
||||
{
|
||||
ApplyBandExtensionChannel(&block->Channels[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyBandExtensionChannel(channel* channel)
|
||||
{
|
||||
const int groupAUnit = channel->Block->QuantizationUnitCount;
|
||||
int* scaleFactors = channel->ScaleFactors;
|
||||
double* spectra = channel->Spectra;
|
||||
double scales[6];
|
||||
int* values = channel->BexValues;
|
||||
|
||||
const bex_group* bex_info = &BexGroupInfo[channel->Block->QuantizationUnitCount - 13];
|
||||
const int bandCount = bex_info->band_count;
|
||||
const int groupBUnit = bex_info->group_b_unit;
|
||||
const int groupCUnit = bex_info->group_c_unit;
|
||||
|
||||
const int totalUnits = max(groupCUnit, 22);
|
||||
const int bexQuantUnits = totalUnits - groupAUnit;
|
||||
|
||||
const int groupABin = QuantUnitToCoeffIndex[groupAUnit];
|
||||
const int groupBBin = QuantUnitToCoeffIndex[groupBUnit];
|
||||
const int groupCBin = QuantUnitToCoeffIndex[groupCUnit];
|
||||
const int totalBins = QuantUnitToCoeffIndex[totalUnits];
|
||||
|
||||
FillHighFrequencies(spectra, groupABin, groupBBin, groupCBin, totalBins);
|
||||
|
||||
double groupAScale, groupBScale, groupCScale;
|
||||
double rate, scale, mult;
|
||||
|
||||
switch (channel->BexMode)
|
||||
{
|
||||
case 0:
|
||||
switch (bandCount)
|
||||
{
|
||||
case 3:
|
||||
scales[0] = BexMode0Bands3[0][values[0]];
|
||||
scales[1] = BexMode0Bands3[1][values[0]];
|
||||
scales[2] = BexMode0Bands3[2][values[1]];
|
||||
scales[3] = BexMode0Bands3[3][values[2]];
|
||||
scales[4] = BexMode0Bands3[4][values[3]];
|
||||
break;
|
||||
case 4:
|
||||
scales[0] = BexMode0Bands4[0][values[0]];
|
||||
scales[1] = BexMode0Bands4[1][values[0]];
|
||||
scales[2] = BexMode0Bands4[2][values[1]];
|
||||
scales[3] = BexMode0Bands4[3][values[2]];
|
||||
scales[4] = BexMode0Bands4[4][values[3]];
|
||||
break;
|
||||
case 5:
|
||||
scales[0] = BexMode0Bands5[0][values[0]];
|
||||
scales[1] = BexMode0Bands5[1][values[1]];
|
||||
scales[2] = BexMode0Bands5[2][values[1]];
|
||||
break;
|
||||
}
|
||||
|
||||
scales[bexQuantUnits - 1] = SpectrumScale[scaleFactors[groupAUnit]];
|
||||
|
||||
AddNoiseToSpectrum(channel, QuantUnitToCoeffIndex[totalUnits - 1],
|
||||
QuantUnitToCoeffCount[totalUnits - 1]);
|
||||
ScaleBexQuantUnits(spectra, scales, groupAUnit, totalUnits);
|
||||
break;
|
||||
case 1:
|
||||
for (int i = groupAUnit; i < totalUnits; i++)
|
||||
{
|
||||
scales[i - groupAUnit] = SpectrumScale[scaleFactors[i]];
|
||||
}
|
||||
|
||||
AddNoiseToSpectrum(channel, groupABin, totalBins - groupABin);
|
||||
ScaleBexQuantUnits(spectra, scales, groupAUnit, totalUnits);
|
||||
break;
|
||||
case 2:
|
||||
groupAScale = BexMode2Scale[values[0]];
|
||||
groupBScale = BexMode2Scale[values[1]];
|
||||
|
||||
for (int i = groupABin; i < groupBBin; i++)
|
||||
{
|
||||
spectra[i] *= groupAScale;
|
||||
}
|
||||
|
||||
for (int i = groupBBin; i < groupCBin; i++)
|
||||
{
|
||||
spectra[i] *= groupBScale;
|
||||
}
|
||||
return;
|
||||
case 3:
|
||||
rate = pow(2, BexMode3Rate[values[1]]);
|
||||
scale = BexMode3Initial[values[0]];
|
||||
for (int i = groupABin; i < totalBins; i++)
|
||||
{
|
||||
scale *= rate;
|
||||
spectra[i] *= scale;
|
||||
}
|
||||
return;
|
||||
case 4:
|
||||
mult = BexMode4Multiplier[values[0]];
|
||||
groupAScale = 0.7079468 * mult;
|
||||
groupBScale = 0.5011902 * mult;
|
||||
groupCScale = 0.3548279 * mult;
|
||||
|
||||
for (int i = groupABin; i < groupBBin; i++)
|
||||
{
|
||||
spectra[i] *= groupAScale;
|
||||
}
|
||||
|
||||
for (int i = groupBBin; i < groupCBin; i++)
|
||||
{
|
||||
spectra[i] *= groupBScale;
|
||||
}
|
||||
|
||||
for (int i = groupCBin; i < totalBins; i++)
|
||||
{
|
||||
spectra[i] *= groupCScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScaleBexQuantUnits(double* spectra, double* scales, int startUnit, int totalUnits)
|
||||
{
|
||||
for (int i = startUnit; i < totalUnits; i++)
|
||||
{
|
||||
for (int k = QuantUnitToCoeffIndex[i]; k < QuantUnitToCoeffIndex[i + 1]; k++)
|
||||
{
|
||||
spectra[k] *= scales[i - startUnit];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FillHighFrequencies(double* spectra, int groupABin, int groupBBin, int groupCBin, int totalBins)
|
||||
{
|
||||
for (int i = 0; i < groupBBin - groupABin; i++)
|
||||
{
|
||||
spectra[groupABin + i] = spectra[groupABin - i - 1];
|
||||
}
|
||||
|
||||
for (int i = 0; i < groupCBin - groupBBin; i++)
|
||||
{
|
||||
spectra[groupBBin + i] = spectra[groupBBin - i - 1];
|
||||
}
|
||||
|
||||
for (int i = 0; i < totalBins - groupCBin; i++)
|
||||
{
|
||||
spectra[groupCBin + i] = spectra[groupCBin - i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
void AddNoiseToSpectrum(channel* channel, int index, int count)
|
||||
{
|
||||
if (!channel->rng.initialized)
|
||||
{
|
||||
int* sf = channel->ScaleFactors;
|
||||
const unsigned short seed = (unsigned short)(543 * (sf[8] + sf[12] + sf[15] + 1));
|
||||
rng_init(&channel->rng, seed);
|
||||
}
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
channel->Spectra[i + index] = rng_next(&channel->rng) / 65535.0 * 2.0 - 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
void rng_init(rng_cxt* rng, unsigned short seed)
|
||||
{
|
||||
const int startValue = 0x4D93 * (seed ^ (seed >> 14));
|
||||
|
||||
rng->stateA = (unsigned short)(3 - startValue);
|
||||
rng->stateB = (unsigned short)(2 - startValue);
|
||||
rng->stateC = (unsigned short)(1 - startValue);
|
||||
rng->stateD = (unsigned short)(0 - startValue);
|
||||
rng->initialized = TRUE;
|
||||
}
|
||||
|
||||
unsigned short rng_next(rng_cxt* rng)
|
||||
{
|
||||
const unsigned short t = (unsigned short)(rng->stateD ^ (rng->stateD << 5));
|
||||
rng->stateD = rng->stateC;
|
||||
rng->stateC = rng->stateB;
|
||||
rng->stateB = rng->stateA;
|
||||
rng->stateA = (unsigned short)(t ^ rng->stateA ^ ((t ^ (rng->stateA >> 5)) >> 4));
|
||||
return rng->stateA;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "structures.h"
|
||||
|
||||
void ApplyBandExtension(block* block);
|
||||
void ApplyBandExtensionChannel(channel* channel);
|
||||
|
||||
void ScaleBexQuantUnits(double* spectra, double* scales, int startUnit, int totalUnits);
|
||||
void FillHighFrequencies(double* spectra, int groupABin, int groupBBin, int groupCBin, int totalBins);
|
||||
void AddNoiseToSpectrum(channel* channel, int index, int count);
|
||||
|
||||
void rng_init(rng_cxt* rng, unsigned short seed);
|
||||
unsigned short rng_next(rng_cxt* rng);
|
|
@ -0,0 +1,119 @@
|
|||
#include "bit_allocation.h"
|
||||
#include "tables.h"
|
||||
#include "utility.h"
|
||||
#include <string.h>
|
||||
|
||||
at9_status CreateGradient(block* block)
|
||||
{
|
||||
int valueCount = block->GradientEndValue - block->GradientStartValue;
|
||||
int unitCount = block->GradientEndUnit - block->GradientStartUnit;
|
||||
|
||||
for (int i = 0; i < block->GradientEndUnit; i++)
|
||||
{
|
||||
block->Gradient[i] = block->GradientStartValue;
|
||||
}
|
||||
|
||||
for (int i = block->GradientEndUnit; i <= block->QuantizationUnitCount; i++)
|
||||
{
|
||||
block->Gradient[i] = block->GradientEndValue;
|
||||
}
|
||||
if (unitCount <= 0) return ERR_SUCCESS;
|
||||
if (valueCount == 0) return ERR_SUCCESS;
|
||||
|
||||
const unsigned char* curve = GradientCurves[unitCount - 1];
|
||||
if (valueCount <= 0)
|
||||
{
|
||||
double scale = (-valueCount - 1) / 31.0;
|
||||
int baseVal = block->GradientStartValue - 1;
|
||||
for (int i = block->GradientStartUnit; i < block->GradientEndUnit; i++)
|
||||
{
|
||||
block->Gradient[i] = baseVal - (int)(curve[i - block->GradientStartUnit] * scale);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
double scale = (valueCount - 1) / 31.0;
|
||||
int baseVal = block->GradientStartValue + 1;
|
||||
for (int i = block->GradientStartUnit; i < block->GradientEndUnit; i++)
|
||||
{
|
||||
block->Gradient[i] = baseVal + (int)(curve[i - block->GradientStartUnit] * scale);
|
||||
}
|
||||
}
|
||||
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
void CalculateMask(channel* channel)
|
||||
{
|
||||
memset(channel->PrecisionMask, 0, sizeof(channel->PrecisionMask));
|
||||
for (int i = 1; i < channel->Block->QuantizationUnitCount; i++)
|
||||
{
|
||||
const int delta = channel->ScaleFactors[i] - channel->ScaleFactors[i - 1];
|
||||
if (delta > 1)
|
||||
{
|
||||
channel->PrecisionMask[i] += min(delta - 1, 5);
|
||||
}
|
||||
else if (delta < -1)
|
||||
{
|
||||
channel->PrecisionMask[i - 1] += min(delta * -1 - 1, 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CalculatePrecisions(channel* channel)
|
||||
{
|
||||
block* block = channel->Block;
|
||||
|
||||
if (block->GradientMode != 0)
|
||||
{
|
||||
for (int i = 0; i < block->QuantizationUnitCount; i++)
|
||||
{
|
||||
channel->Precisions[i] = channel->ScaleFactors[i] + channel->PrecisionMask[i] - block->Gradient[i];
|
||||
if (channel->Precisions[i] > 0)
|
||||
{
|
||||
switch (block->GradientMode)
|
||||
{
|
||||
case 1:
|
||||
channel->Precisions[i] /= 2;
|
||||
break;
|
||||
case 2:
|
||||
channel->Precisions[i] = 3 * channel->Precisions[i] / 8;
|
||||
break;
|
||||
case 3:
|
||||
channel->Precisions[i] /= 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < block->QuantizationUnitCount; i++)
|
||||
{
|
||||
channel->Precisions[i] = channel->ScaleFactors[i] - block->Gradient[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < block->QuantizationUnitCount; i++)
|
||||
{
|
||||
if (channel->Precisions[i] < 1)
|
||||
{
|
||||
channel->Precisions[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < block->GradientBoundary; i++)
|
||||
{
|
||||
channel->Precisions[i]++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < block->QuantizationUnitCount; i++)
|
||||
{
|
||||
channel->PrecisionsFine[i] = 0;
|
||||
if (channel->Precisions[i] > 15)
|
||||
{
|
||||
channel->PrecisionsFine[i] = channel->Precisions[i] - 15;
|
||||
channel->Precisions[i] = 15;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
#include "unpack.h"
|
||||
|
||||
at9_status CreateGradient(block* block);
|
||||
void CalculateMask(channel* channel);
|
||||
void CalculatePrecisions(channel* channel);
|
|
@ -0,0 +1,110 @@
|
|||
#include "bit_reader.h"
|
||||
#include "utility.h"
|
||||
|
||||
static int peek_int_fallback(bit_reader_cxt* br, int bit_count);
|
||||
|
||||
void init_bit_reader_cxt(bit_reader_cxt* br, const void * buffer)
|
||||
{
|
||||
br->buffer = buffer;
|
||||
br->position = 0;
|
||||
}
|
||||
|
||||
int read_int(bit_reader_cxt* br, const int bits)
|
||||
{
|
||||
const int value = peek_int(br, bits);
|
||||
br->position += bits;
|
||||
return value;
|
||||
}
|
||||
|
||||
int read_signed_int(bit_reader_cxt* br, const int bits)
|
||||
{
|
||||
const int value = peek_int(br, bits);
|
||||
br->position += bits;
|
||||
return SignExtend32(value, bits);
|
||||
}
|
||||
|
||||
int read_offset_binary(bit_reader_cxt* br, const int bits)
|
||||
{
|
||||
const int offset = 1 << (bits - 1);
|
||||
const int value = peek_int(br, bits) - offset;
|
||||
br->position += bits;
|
||||
return value;
|
||||
}
|
||||
|
||||
int peek_int(bit_reader_cxt* br, const int bits)
|
||||
{
|
||||
const int byte_index = br->position / 8;
|
||||
const int bit_index = br->position % 8;
|
||||
const unsigned char* buffer = br->buffer;
|
||||
|
||||
if (bits <= 9)
|
||||
{
|
||||
int value = buffer[byte_index] << 8 | buffer[byte_index + 1];
|
||||
value &= 0xFFFF >> bit_index;
|
||||
value >>= 16 - bits - bit_index;
|
||||
return value;
|
||||
}
|
||||
|
||||
if (bits <= 17)
|
||||
{
|
||||
int value = buffer[byte_index] << 16 | buffer[byte_index + 1] << 8 | buffer[byte_index + 2];
|
||||
value &= 0xFFFFFF >> bit_index;
|
||||
value >>= 24 - bits - bit_index;
|
||||
return value;
|
||||
}
|
||||
|
||||
if (bits <= 25)
|
||||
{
|
||||
int value = buffer[byte_index] << 24
|
||||
| buffer[byte_index + 1] << 16
|
||||
| buffer[byte_index + 2] << 8
|
||||
| buffer[byte_index + 3];
|
||||
|
||||
value &= (int)(0xFFFFFFFF >> bit_index);
|
||||
value >>= 32 - bits - bit_index;
|
||||
return value;
|
||||
}
|
||||
return peek_int_fallback(br, bits);
|
||||
}
|
||||
|
||||
void align_position(bit_reader_cxt* br, const unsigned int multiple)
|
||||
{
|
||||
const int position = br->position;
|
||||
if (position % multiple == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
br->position = position + multiple - position % multiple;
|
||||
}
|
||||
|
||||
static int peek_int_fallback(bit_reader_cxt* br, int bit_count)
|
||||
{
|
||||
int value = 0;
|
||||
int byte_index = br->position / 8;
|
||||
int bit_index = br->position % 8;
|
||||
const unsigned char* buffer = br->buffer;
|
||||
|
||||
while (bit_count > 0)
|
||||
{
|
||||
if (bit_index >= 8)
|
||||
{
|
||||
bit_index = 0;
|
||||
byte_index++;
|
||||
}
|
||||
|
||||
int bits_to_read = bit_count;
|
||||
if (bits_to_read > 8 - bit_index)
|
||||
{
|
||||
bits_to_read = 8 - bit_index;
|
||||
}
|
||||
|
||||
const int mask = 0xFF >> bit_index;
|
||||
const int current_byte = (mask & buffer[byte_index]) >> (8 - bit_index - bits_to_read);
|
||||
|
||||
value = (value << bits_to_read) | current_byte;
|
||||
bit_index += bits_to_read;
|
||||
bit_count -= bits_to_read;
|
||||
}
|
||||
return value;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
typedef struct {
|
||||
const unsigned char * buffer;
|
||||
int position;
|
||||
} bit_reader_cxt;
|
||||
|
||||
void init_bit_reader_cxt(bit_reader_cxt* br, const void * buffer);
|
||||
int peek_int(bit_reader_cxt* br, const int bits);
|
||||
int read_int(bit_reader_cxt* br, const int bits);
|
||||
int read_signed_int(bit_reader_cxt* br, const int bits);
|
||||
int read_offset_binary(bit_reader_cxt* br, const int bits);
|
||||
void align_position(bit_reader_cxt* br, const unsigned int multiple);
|
|
@ -0,0 +1,188 @@
|
|||
#include "bit_reader.h"
|
||||
#include "decinit.h"
|
||||
#include "error_codes.h"
|
||||
#include "structures.h"
|
||||
#include "tables.h"
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "utility.h"
|
||||
|
||||
static int BlockTypeToChannelCount(BlockType block_type);
|
||||
|
||||
at9_status init_decoder(atrac9_handle* handle, unsigned char* config_data, int wlength)
|
||||
{
|
||||
ERROR_CHECK(init_config_data(&handle->config, config_data));
|
||||
ERROR_CHECK(init_frame(handle));
|
||||
init_mdct_tables(handle->config.FrameSamplesPower);
|
||||
init_huffman_codebooks();
|
||||
handle->wlength = wlength;
|
||||
handle->initialized = 1;
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
at9_status init_config_data(ConfigData* config, unsigned char* config_data)
|
||||
{
|
||||
memcpy(config->ConfigData, config_data, CONFIG_DATA_SIZE);
|
||||
ERROR_CHECK(read_config_data(config));
|
||||
|
||||
config->FramesPerSuperframe = 1 << config->SuperframeIndex;
|
||||
config->SuperframeBytes = config->FrameBytes << config->SuperframeIndex;
|
||||
|
||||
config->ChannelConfig = ChannelConfigs[config->ChannelConfigIndex];
|
||||
config->ChannelCount = config->ChannelConfig.ChannelCount;
|
||||
config->SampleRate = SampleRates[config->SampleRateIndex];
|
||||
config->HighSampleRate = config->SampleRateIndex > 7;
|
||||
config->FrameSamplesPower = SamplingRateIndexToFrameSamplesPower[config->SampleRateIndex];
|
||||
config->FrameSamples = 1 << config->FrameSamplesPower;
|
||||
config->SuperframeSamples = config->FrameSamples * config->FramesPerSuperframe;
|
||||
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
at9_status read_config_data(ConfigData* config)
|
||||
{
|
||||
bit_reader_cxt br;
|
||||
init_bit_reader_cxt(&br, &config->ConfigData);
|
||||
|
||||
const int header = read_int(&br, 8);
|
||||
config->SampleRateIndex = read_int(&br, 4);
|
||||
config->ChannelConfigIndex = read_int(&br, 3);
|
||||
const int validation_bit = read_int(&br, 1);
|
||||
config->FrameBytes = read_int(&br, 11) + 1;
|
||||
config->SuperframeIndex = read_int(&br, 2);
|
||||
|
||||
if (header != 0xFE || validation_bit != 0)
|
||||
{
|
||||
return ERR_BAD_CONFIG_DATA;
|
||||
}
|
||||
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
at9_status init_frame(atrac9_handle* handle)
|
||||
{
|
||||
const int block_count = handle->config.ChannelConfig.BlockCount;
|
||||
handle->frame.config = &handle->config;
|
||||
|
||||
for (int i = 0; i < block_count; i++)
|
||||
{
|
||||
ERROR_CHECK(init_block(&handle->frame.Blocks[i], &handle->frame, i));
|
||||
}
|
||||
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
at9_status init_block(block* block, frame* parent_frame, int block_index)
|
||||
{
|
||||
block->Frame = parent_frame;
|
||||
block->BlockIndex = block_index;
|
||||
block->config = parent_frame->config;
|
||||
block->BlockType = block->config->ChannelConfig.Types[block_index];
|
||||
block->ChannelCount = BlockTypeToChannelCount(block->BlockType);
|
||||
|
||||
for (int i = 0; i < block->ChannelCount; i++)
|
||||
{
|
||||
ERROR_CHECK(init_channel(&block->Channels[i], block, i));
|
||||
}
|
||||
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
at9_status init_channel(channel* channel, block* parent_block, int channel_index)
|
||||
{
|
||||
channel->Block = parent_block;
|
||||
channel->Frame = parent_block->Frame;
|
||||
channel->config = parent_block->config;
|
||||
channel->ChannelIndex = channel_index;
|
||||
channel->mdct.bits = parent_block->config->FrameSamplesPower;
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
void init_huffman_codebooks()
|
||||
{
|
||||
init_huffman_set(HuffmanScaleFactorsUnsigned, sizeof(HuffmanScaleFactorsUnsigned) / sizeof(HuffmanCodebook));
|
||||
init_huffman_set(HuffmanScaleFactorsSigned, sizeof(HuffmanScaleFactorsSigned) / sizeof(HuffmanCodebook));
|
||||
init_huffman_set(HuffmanSpectrum, sizeof(HuffmanSpectrum) / sizeof(HuffmanCodebook));
|
||||
}
|
||||
|
||||
void init_huffman_set(const HuffmanCodebook* codebooks, int count)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
InitHuffmanCodebook(&codebooks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void init_mdct_tables(int frameSizePower)
|
||||
{
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
GenerateTrigTables(i);
|
||||
GenerateShuffleTable(i);
|
||||
}
|
||||
GenerateMdctWindow(frameSizePower);
|
||||
GenerateImdctWindow(frameSizePower);
|
||||
}
|
||||
|
||||
void GenerateTrigTables(int sizeBits)
|
||||
{
|
||||
const int size = 1 << sizeBits;
|
||||
double* sinTab = SinTables[sizeBits];
|
||||
double* cosTab = CosTables[sizeBits];
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
const double value = M_PI * (4 * i + 1) / (4 * size);
|
||||
sinTab[i] = sin(value);
|
||||
cosTab[i] = cos(value);
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateShuffleTable(int sizeBits)
|
||||
{
|
||||
const int size = 1 << sizeBits;
|
||||
int* table = ShuffleTables[sizeBits];
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
table[i] = BitReverse32(i ^ (i / 2), sizeBits);
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateMdctWindow(int frameSizePower)
|
||||
{
|
||||
const int frameSize = 1 << frameSizePower;
|
||||
double* mdct = MdctWindow[frameSizePower - 6];
|
||||
|
||||
for (int i = 0; i < frameSize; i++)
|
||||
{
|
||||
mdct[i] = (sin(((i + 0.5) / frameSize - 0.5) * M_PI) + 1.0) * 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateImdctWindow(int frameSizePower)
|
||||
{
|
||||
const int frameSize = 1 << frameSizePower;
|
||||
double* imdct = ImdctWindow[frameSizePower - 6];
|
||||
double* mdct = MdctWindow[frameSizePower - 6];
|
||||
|
||||
for (int i = 0; i < frameSize; i++)
|
||||
{
|
||||
imdct[i] = mdct[i] / (mdct[frameSize - 1 - i] * mdct[frameSize - 1 - i] + mdct[i] * mdct[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int BlockTypeToChannelCount(BlockType block_type)
|
||||
{
|
||||
switch (block_type)
|
||||
{
|
||||
case Mono:
|
||||
return 1;
|
||||
case Stereo:
|
||||
return 2;
|
||||
case LFE:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "error_codes.h"
|
||||
#include "structures.h"
|
||||
#include "huffCodes.h"
|
||||
|
||||
at9_status init_decoder(atrac9_handle* handle, unsigned char * config_data, int wlength);
|
||||
at9_status init_config_data(ConfigData* config, unsigned char * config_data);
|
||||
at9_status read_config_data(ConfigData* config);
|
||||
at9_status init_frame(atrac9_handle* handle);
|
||||
at9_status init_block(block* block, frame* parent_frame, int block_index);
|
||||
at9_status init_channel(channel* channel, block* parent_block, int channel_index);
|
||||
void init_huffman_codebooks();
|
||||
void init_huffman_set(const HuffmanCodebook* codebooks, int count);
|
||||
void GenerateTrigTables(int sizeBits);
|
||||
void GenerateShuffleTable(int sizeBits);
|
||||
void init_mdct_tables(int frameSizePower);
|
||||
void GenerateMdctWindow(int frameSizePower);
|
||||
void GenerateImdctWindow(int frameSizePower);
|
|
@ -0,0 +1,114 @@
|
|||
#include <string.h>
|
||||
#include "decoder.h"
|
||||
#include "unpack.h"
|
||||
#include "quantization.h"
|
||||
#include "tables.h"
|
||||
#include "imdct.h"
|
||||
#include <math.h>
|
||||
#include "utility.h"
|
||||
#include "band_extension.h"
|
||||
|
||||
at9_status Decode(atrac9_handle* handle, const unsigned char* audio, unsigned char* pcm, int* bytesUsed)
|
||||
{
|
||||
handle->frame.frameNum++;
|
||||
bit_reader_cxt br;
|
||||
init_bit_reader_cxt(&br, audio);
|
||||
ERROR_CHECK(DecodeFrame(&handle->frame, &br));
|
||||
|
||||
PcmFloatToShort(&handle->frame, (short*)pcm);
|
||||
|
||||
*bytesUsed = br.position / 8;
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
at9_status DecodeFrame(frame* frame, bit_reader_cxt* br)
|
||||
{
|
||||
ERROR_CHECK(UnpackFrame(frame, br));
|
||||
|
||||
for (int i = 0; i < frame->config->ChannelConfig.BlockCount; i++)
|
||||
{
|
||||
block* block = &frame->Blocks[i];
|
||||
|
||||
DequantizeSpectra(block);
|
||||
ApplyIntensityStereo(block);
|
||||
ScaleSpectrumBlock(block);
|
||||
ApplyBandExtension(block);
|
||||
ImdctBlock(block);
|
||||
}
|
||||
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
void PcmFloatToShort(frame* frame, short* pcmOut)
|
||||
{
|
||||
const int endSample = frame->config->FrameSamples;
|
||||
short* dest = pcmOut;
|
||||
for (int d = 0, s = 0; s < endSample; d++, s++)
|
||||
{
|
||||
for (int i = 0; i < frame->config->ChannelConfig.BlockCount; i++)
|
||||
{
|
||||
block* block = &frame->Blocks[i];
|
||||
|
||||
for (int j = 0; j < block->ChannelCount; j++)
|
||||
{
|
||||
channel* channel = &block->Channels[j];
|
||||
double* pcmSrc = channel->Pcm;
|
||||
|
||||
const double sample = pcmSrc[d];
|
||||
const int roundedSample = (int)floor(sample + 0.5);
|
||||
*dest++ = Clamp16(roundedSample);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ImdctBlock(block* block)
|
||||
{
|
||||
for (int i = 0; i < block->ChannelCount; i++)
|
||||
{
|
||||
channel* channel = &block->Channels[i];
|
||||
|
||||
RunImdct(&channel->mdct, channel->Spectra, channel->Pcm);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyIntensityStereo(block* block)
|
||||
{
|
||||
if (block->BlockType != Stereo) return;
|
||||
|
||||
const int totalUnits = block->QuantizationUnitCount;
|
||||
const int stereoUnits = block->StereoQuantizationUnit;
|
||||
if (stereoUnits >= totalUnits) return;
|
||||
|
||||
channel* source = &block->Channels[block->PrimaryChannelIndex == 0 ? 0 : 1];
|
||||
channel* dest = &block->Channels[block->PrimaryChannelIndex == 0 ? 1 : 0];
|
||||
|
||||
for (int i = stereoUnits; i < totalUnits; i++)
|
||||
{
|
||||
const int sign = block->JointStereoSigns[i];
|
||||
for (int sb = QuantUnitToCoeffIndex[i]; sb < QuantUnitToCoeffIndex[i + 1]; sb++)
|
||||
{
|
||||
if (sign > 0)
|
||||
{
|
||||
dest->Spectra[sb] = -source->Spectra[sb];
|
||||
}
|
||||
else
|
||||
{
|
||||
dest->Spectra[sb] = source->Spectra[sb];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int GetCodecInfo(atrac9_handle* handle, CodecInfo * pCodecInfo)
|
||||
{
|
||||
pCodecInfo->channels = handle->config.ChannelCount;
|
||||
pCodecInfo->channelConfigIndex = handle->config.ChannelConfigIndex;
|
||||
pCodecInfo->samplingRate = handle->config.SampleRate;
|
||||
pCodecInfo->superframeSize = handle->config.SuperframeBytes;
|
||||
pCodecInfo->framesInSuperframe = handle->config.FramesPerSuperframe;
|
||||
pCodecInfo->frameSamples = handle->config.FrameSamples;
|
||||
pCodecInfo->wlength = handle->wlength;
|
||||
memcpy(pCodecInfo->configData, handle->config.ConfigData, CONFIG_DATA_SIZE);
|
||||
return ERR_SUCCESS;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
#include "bit_reader.h"
|
||||
#include "error_codes.h"
|
||||
#include "structures.h"
|
||||
|
||||
at9_status Decode(atrac9_handle* handle, const unsigned char* audio, unsigned char* pcm, int* bytesUsed);
|
||||
at9_status DecodeFrame(frame* frame, bit_reader_cxt* br);
|
||||
void ImdctBlock(block* block);
|
||||
void ApplyIntensityStereo(block* block);
|
||||
void PcmFloatToShort(frame* frame, short* pcmOut);
|
||||
int GetCodecInfo(atrac9_handle* handle, CodecInfo* pCodecInfo);
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
typedef enum at9_status
|
||||
{
|
||||
ERR_SUCCESS = 0,
|
||||
|
||||
ERR_NOT_IMPLEMENTED = 0x80000000,
|
||||
|
||||
ERR_BAD_CONFIG_DATA = 0x81000000,
|
||||
|
||||
ERR_UNPACK_SUPERFRAME_FLAG_INVALID = 0x82000000,
|
||||
ERR_UNPACK_REUSE_BAND_PARAMS_INVALID,
|
||||
ERR_UNPACK_BAND_PARAMS_INVALID,
|
||||
|
||||
ERR_UNPACK_GRAD_BOUNDARY_INVALID = 0x82100000,
|
||||
ERR_UNPACK_GRAD_START_UNIT_OOB,
|
||||
ERR_UNPACK_GRAD_END_UNIT_OOB,
|
||||
ERR_UNPACK_GRAD_START_VALUE_OOB,
|
||||
ERR_UNPACK_GRAD_END_VALUE_OOB,
|
||||
ERR_UNPACK_GRAD_END_UNIT_INVALID, // start_unit > end_unit
|
||||
|
||||
ERR_UNPACK_SCALE_FACTOR_MODE_INVALID,
|
||||
ERR_UNPACK_SCALE_FACTOR_OOB,
|
||||
|
||||
ERR_UNPACK_EXTENSION_DATA_INVALID
|
||||
} at9_status;
|
||||
|
||||
#define ERROR_CHECK(x) do { \
|
||||
at9_status status = (x); \
|
||||
if (status != ERR_SUCCESS) { \
|
||||
return status; \
|
||||
} \
|
||||
} while (0)
|
|
@ -0,0 +1,152 @@
|
|||
#include "huffCodes.h"
|
||||
#include "utility.h"
|
||||
|
||||
int ReadHuffmanValue(const HuffmanCodebook* huff, bit_reader_cxt* br, int is_signed)
|
||||
{
|
||||
const int code = peek_int(br, huff->MaxBitSize);
|
||||
const unsigned char value = huff->Lookup[code];
|
||||
const int bits = huff->Bits[value];
|
||||
br->position += bits;
|
||||
return is_signed ? SignExtend32(value, huff->ValueBits) : value;
|
||||
}
|
||||
|
||||
void DecodeHuffmanValues(int* spectrum, int index, int bandCount, const HuffmanCodebook* huff, const int* values)
|
||||
{
|
||||
const int valueCount = bandCount >> huff->ValueCountPower;
|
||||
const int mask = (1 << huff->ValueBits) - 1;
|
||||
|
||||
for (int i = 0; i < valueCount; i++)
|
||||
{
|
||||
int value = values[i];
|
||||
for (int j = 0; j < huff->ValueCount; j++)
|
||||
{
|
||||
spectrum[index++] = SignExtend32(value & mask, huff->ValueBits);
|
||||
value >>= huff->ValueBits;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InitHuffmanCodebook(const HuffmanCodebook* codebook)
|
||||
{
|
||||
const int huffLength = codebook->Length;
|
||||
if (huffLength == 0) return;
|
||||
|
||||
unsigned char* dest = codebook->Lookup;
|
||||
|
||||
for (int i = 0; i < huffLength; i++)
|
||||
{
|
||||
if (codebook->Bits[i] == 0) continue;
|
||||
const int unusedBits = codebook->MaxBitSize - codebook->Bits[i];
|
||||
|
||||
const int start = codebook->Codes[i] << unusedBits;
|
||||
const int length = 1 << unusedBits;
|
||||
const int end = start + length;
|
||||
|
||||
for (int j = start; j < end; j++)
|
||||
{
|
||||
dest[j] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HuffmanCodebook HuffmanScaleFactorsUnsigned[7] = {
|
||||
{0},
|
||||
{ScaleFactorsA1Bits, ScaleFactorsA1Codes, ScaleFactorsA1Lookup, 2, 1, 0, 1, 2, 1},
|
||||
{ScaleFactorsA2Bits, ScaleFactorsA2Codes, ScaleFactorsA2Lookup, 4, 1, 0, 2, 4, 3},
|
||||
{ScaleFactorsA3Bits, ScaleFactorsA3Codes, ScaleFactorsA3Lookup, 8, 1, 0, 3, 8, 6},
|
||||
{ScaleFactorsA4Bits, ScaleFactorsA4Codes, ScaleFactorsA4Lookup, 16, 1, 0, 4, 16, 8},
|
||||
{ScaleFactorsA5Bits, ScaleFactorsA5Codes, ScaleFactorsA5Lookup, 32, 1, 0, 5, 32, 8},
|
||||
{ScaleFactorsA6Bits, ScaleFactorsA6Codes, ScaleFactorsA6Lookup, 64, 1, 0, 6, 64, 8},
|
||||
};
|
||||
|
||||
HuffmanCodebook HuffmanScaleFactorsSigned[6] = {
|
||||
{0},
|
||||
{0},
|
||||
{ScaleFactorsB2Bits, ScaleFactorsB2Codes, ScaleFactorsB2Lookup, 4, 1, 0, 2, 4, 2},
|
||||
{ScaleFactorsB3Bits, ScaleFactorsB3Codes, ScaleFactorsB3Lookup, 8, 1, 0, 3, 8, 6},
|
||||
{ScaleFactorsB4Bits, ScaleFactorsB4Codes, ScaleFactorsB4Lookup, 16, 1, 0, 4, 16, 8},
|
||||
{ScaleFactorsB5Bits, ScaleFactorsB5Codes, ScaleFactorsB5Lookup, 32, 1, 0, 5, 32, 8},
|
||||
};
|
||||
|
||||
HuffmanCodebook HuffmanSpectrum[2][8][4] = {
|
||||
{
|
||||
{0},
|
||||
{0},
|
||||
{
|
||||
{SpectrumA21Bits, SpectrumA21Codes, SpectrumA21Lookup, 16, 2, 1, 2, 4, 3},
|
||||
{SpectrumA22Bits, SpectrumA22Codes, SpectrumA22Lookup, 256, 4, 2, 2, 4, 8},
|
||||
{SpectrumA23Bits, SpectrumA23Codes, SpectrumA23Lookup, 256, 4, 2, 2, 4, 9},
|
||||
{SpectrumA24Bits, SpectrumA24Codes, SpectrumA24Lookup, 256, 4, 2, 2, 4, 10}
|
||||
},
|
||||
{
|
||||
{SpectrumA31Bits, SpectrumA31Codes, SpectrumA31Lookup, 64, 2, 1, 3, 8, 7},
|
||||
{SpectrumA32Bits, SpectrumA32Codes, SpectrumA32Lookup, 64, 2, 1, 3, 8, 7},
|
||||
{SpectrumA33Bits, SpectrumA33Codes, SpectrumA33Lookup, 64, 2, 1, 3, 8, 8},
|
||||
{SpectrumA34Bits, SpectrumA34Codes, SpectrumA34Lookup, 64, 2, 1, 3, 8, 10}
|
||||
},
|
||||
{
|
||||
{SpectrumA41Bits, SpectrumA41Codes, SpectrumA41Lookup, 256, 2, 1, 4, 16, 9},
|
||||
{SpectrumA42Bits, SpectrumA42Codes, SpectrumA42Lookup, 256, 2, 1, 4, 16, 10},
|
||||
{SpectrumA43Bits, SpectrumA43Codes, SpectrumA43Lookup, 256, 2, 1, 4, 16, 10},
|
||||
{SpectrumA44Bits, SpectrumA44Codes, SpectrumA44Lookup, 256, 2, 1, 4, 16, 10}
|
||||
},
|
||||
{
|
||||
{SpectrumA51Bits, SpectrumA51Codes, SpectrumA51Lookup, 32, 1, 0, 5, 32, 6},
|
||||
{SpectrumA52Bits, SpectrumA52Codes, SpectrumA52Lookup, 32, 1, 0, 5, 32, 6},
|
||||
{SpectrumA53Bits, SpectrumA53Codes, SpectrumA53Lookup, 32, 1, 0, 5, 32, 7},
|
||||
{SpectrumA54Bits, SpectrumA54Codes, SpectrumA54Lookup, 32, 1, 0, 5, 32, 8}
|
||||
},
|
||||
{
|
||||
{SpectrumA61Bits, SpectrumA61Codes, SpectrumA61Lookup, 64, 1, 0, 6, 64, 7},
|
||||
{SpectrumA62Bits, SpectrumA62Codes, SpectrumA62Lookup, 64, 1, 0, 6, 64, 7},
|
||||
{SpectrumA63Bits, SpectrumA63Codes, SpectrumA63Lookup, 64, 1, 0, 6, 64, 8},
|
||||
{SpectrumA64Bits, SpectrumA64Codes, SpectrumA64Lookup, 64, 1, 0, 6, 64, 9}
|
||||
},
|
||||
{
|
||||
{SpectrumA71Bits, SpectrumA71Codes, SpectrumA71Lookup, 128, 1, 0, 7, 128, 8},
|
||||
{SpectrumA72Bits, SpectrumA72Codes, SpectrumA72Lookup, 128, 1, 0, 7, 128, 8},
|
||||
{SpectrumA73Bits, SpectrumA73Codes, SpectrumA73Lookup, 128, 1, 0, 7, 128, 9},
|
||||
{SpectrumA74Bits, SpectrumA74Codes, SpectrumA74Lookup, 128, 1, 0, 7, 128, 10}
|
||||
}
|
||||
},
|
||||
{
|
||||
{0},
|
||||
{0},
|
||||
{
|
||||
{0},
|
||||
{SpectrumB22Bits, SpectrumB22Codes, SpectrumB22Lookup, 256, 4, 2, 2, 4, 10},
|
||||
{SpectrumB23Bits, SpectrumB23Codes, SpectrumB23Lookup, 256, 4, 2, 2, 4, 10},
|
||||
{SpectrumB24Bits, SpectrumB24Codes, SpectrumB24Lookup, 256, 4, 2, 2, 4, 10}
|
||||
},
|
||||
{
|
||||
{0},
|
||||
{SpectrumB32Bits, SpectrumB32Codes, SpectrumB32Lookup, 64, 2, 1, 3, 8, 9},
|
||||
{SpectrumB33Bits, SpectrumB33Codes, SpectrumB33Lookup, 64, 2, 1, 3, 8, 10},
|
||||
{SpectrumB34Bits, SpectrumB34Codes, SpectrumB34Lookup, 64, 2, 1, 3, 8, 10}
|
||||
},
|
||||
{
|
||||
{0},
|
||||
{SpectrumB42Bits, SpectrumB42Codes, SpectrumB42Lookup, 256, 2, 1, 4, 16, 10},
|
||||
{SpectrumB43Bits, SpectrumB43Codes, SpectrumB43Lookup, 256, 2, 1, 4, 16, 10},
|
||||
{SpectrumB44Bits, SpectrumB44Codes, SpectrumB44Lookup, 256, 2, 1, 4, 16, 10}
|
||||
},
|
||||
{
|
||||
{0},
|
||||
{SpectrumB52Bits, SpectrumB52Codes, SpectrumB52Lookup, 32, 1, 0, 5, 32, 7},
|
||||
{SpectrumB53Bits, SpectrumB53Codes, SpectrumB53Lookup, 32, 1, 0, 5, 32, 8},
|
||||
{SpectrumB54Bits, SpectrumB54Codes, SpectrumB54Lookup, 32, 1, 0, 5, 32, 9}
|
||||
},
|
||||
{
|
||||
{0},
|
||||
{SpectrumB62Bits, SpectrumB62Codes, SpectrumB62Lookup, 64, 1, 0, 6, 64, 8},
|
||||
{SpectrumB63Bits, SpectrumB63Codes, SpectrumB63Lookup, 64, 1, 0, 6, 64, 9},
|
||||
{SpectrumB64Bits, SpectrumB64Codes, SpectrumB64Lookup, 64, 1, 0, 6, 64, 10}
|
||||
},
|
||||
{
|
||||
{0},
|
||||
{SpectrumB72Bits, SpectrumB72Codes, SpectrumB72Lookup, 128, 1, 0, 7, 128, 9},
|
||||
{SpectrumB73Bits, SpectrumB73Codes, SpectrumB73Lookup, 128, 1, 0, 7, 128, 10},
|
||||
{SpectrumB74Bits, SpectrumB74Codes, SpectrumB74Lookup, 128, 1, 0, 7, 128, 10}
|
||||
}
|
||||
}
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,80 @@
|
|||
#include "imdct.h"
|
||||
#include "tables.h"
|
||||
|
||||
void RunImdct(mdct* mdct, double* input, double* output)
|
||||
{
|
||||
const int size = 1 << mdct->bits;
|
||||
const int half = size / 2;
|
||||
double dctOut[MAX_FRAME_SAMPLES];
|
||||
const double* window = ImdctWindow[mdct->bits - 6];
|
||||
double* previous = mdct->_imdctPrevious;
|
||||
|
||||
Dct4(mdct, input, dctOut);
|
||||
|
||||
for (int i = 0; i < half; i++)
|
||||
{
|
||||
output[i] = window[i] * dctOut[i + half] + previous[i];
|
||||
output[i + half] = window[i + half] * -dctOut[size - 1 - i] - previous[i + half];
|
||||
previous[i] = window[size - 1 - i] * -dctOut[half - i - 1];
|
||||
previous[i + half] = window[half - i - 1] * dctOut[i];
|
||||
}
|
||||
}
|
||||
|
||||
void Dct4(mdct* mdct, double* input, double* output)
|
||||
{
|
||||
int MdctBits = mdct->bits;
|
||||
int MdctSize = 1 << MdctBits;
|
||||
const int* shuffleTable = ShuffleTables[MdctBits];
|
||||
const double* sinTable = SinTables[MdctBits];
|
||||
const double* cosTable = CosTables[MdctBits];
|
||||
double dctTemp[MAX_FRAME_SAMPLES];
|
||||
|
||||
int size = MdctSize;
|
||||
int lastIndex = size - 1;
|
||||
int halfSize = size / 2;
|
||||
|
||||
for (int i = 0; i < halfSize; i++)
|
||||
{
|
||||
int i2 = i * 2;
|
||||
double a = input[i2];
|
||||
double b = input[lastIndex - i2];
|
||||
double sin = sinTable[i];
|
||||
double cos = cosTable[i];
|
||||
dctTemp[i2] = a * cos + b * sin;
|
||||
dctTemp[i2 + 1] = a * sin - b * cos;
|
||||
}
|
||||
int stageCount = MdctBits - 1;
|
||||
|
||||
for (int stage = 0; stage < stageCount; stage++)
|
||||
{
|
||||
int blockCount = 1 << stage;
|
||||
int blockSizeBits = stageCount - stage;
|
||||
int blockHalfSizeBits = blockSizeBits - 1;
|
||||
int blockSize = 1 << blockSizeBits;
|
||||
int blockHalfSize = 1 << blockHalfSizeBits;
|
||||
sinTable = SinTables[blockHalfSizeBits];
|
||||
cosTable = CosTables[blockHalfSizeBits];
|
||||
|
||||
for (int block = 0; block < blockCount; block++)
|
||||
{
|
||||
for (int i = 0; i < blockHalfSize; i++)
|
||||
{
|
||||
int frontPos = (block * blockSize + i) * 2;
|
||||
int backPos = frontPos + blockSize;
|
||||
double a = dctTemp[frontPos] - dctTemp[backPos];
|
||||
double b = dctTemp[frontPos + 1] - dctTemp[backPos + 1];
|
||||
double sin = sinTable[i];
|
||||
double cos = cosTable[i];
|
||||
dctTemp[frontPos] += dctTemp[backPos];
|
||||
dctTemp[frontPos + 1] += dctTemp[backPos + 1];
|
||||
dctTemp[backPos] = a * cos + b * sin;
|
||||
dctTemp[backPos + 1] = a * sin - b * cos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < MdctSize; i++)
|
||||
{
|
||||
output[i] = dctTemp[shuffleTable[i]];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "structures.h"
|
||||
|
||||
void RunImdct(mdct* mdct, double* input, double* output);
|
||||
void Dct4(mdct* mdct, double* input, double* output);
|
|
@ -0,0 +1,33 @@
|
|||
#include "libatrac9.h"
|
||||
#include "structures.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "decinit.h"
|
||||
#include "decoder.h"
|
||||
|
||||
void* Atrac9GetHandle()
|
||||
{
|
||||
struct atrac9_handle* handle = malloc(sizeof(atrac9_handle));
|
||||
memset(handle, 0, sizeof(atrac9_handle));
|
||||
return handle;
|
||||
}
|
||||
|
||||
void Atrac9ReleaseHandle(void* handle)
|
||||
{
|
||||
free(handle);
|
||||
}
|
||||
|
||||
int Atrac9InitDecoder(void* handle, unsigned char * pConfigData)
|
||||
{
|
||||
return init_decoder(handle, pConfigData, 16);
|
||||
}
|
||||
|
||||
int Atrac9Decode(void* handle, const unsigned char *pAtrac9Buffer, short *pPcmBuffer, int *pNBytesUsed)
|
||||
{
|
||||
return Decode(handle, pAtrac9Buffer, (unsigned char*)pPcmBuffer, pNBytesUsed);
|
||||
}
|
||||
|
||||
int Atrac9GetCodecInfo(void* handle, Atrac9CodecInfo * pCodecInfo)
|
||||
{
|
||||
return GetCodecInfo(handle, (CodecInfo*)pCodecInfo);
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ATRAC9_CONFIG_DATA_SIZE 4
|
||||
|
||||
typedef struct {
|
||||
int channels;
|
||||
int channelConfigIndex;
|
||||
int samplingRate;
|
||||
int superframeSize;
|
||||
int framesInSuperframe;
|
||||
int frameSamples;
|
||||
int wlength;
|
||||
unsigned char configData[ATRAC9_CONFIG_DATA_SIZE];
|
||||
|
||||
double MdctWindow[3][256];
|
||||
double ImdctWindow[3][256];
|
||||
} Atrac9CodecInfo;
|
||||
|
||||
void* Atrac9GetHandle(void);
|
||||
void Atrac9ReleaseHandle(void* handle);
|
||||
|
||||
int Atrac9InitDecoder(void* handle, unsigned char *pConfigData);
|
||||
int Atrac9Decode(void* handle, const unsigned char *pAtrac9Buffer, short *pPcmBuffer, int *pNBytesUsed);
|
||||
|
||||
int Atrac9GetCodecInfo(void* handle, Atrac9CodecInfo *pCodecInfo);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27130.2010
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libatrac9", "libatrac9.vcxproj", "{2425F2CC-BB1B-4069-BC10-1C7F535EF8E8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{2425F2CC-BB1B-4069-BC10-1C7F535EF8E8}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{2425F2CC-BB1B-4069-BC10-1C7F535EF8E8}.Debug|x64.Build.0 = Debug|x64
|
||||
{2425F2CC-BB1B-4069-BC10-1C7F535EF8E8}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{2425F2CC-BB1B-4069-BC10-1C7F535EF8E8}.Debug|x86.Build.0 = Debug|Win32
|
||||
{2425F2CC-BB1B-4069-BC10-1C7F535EF8E8}.Release|x64.ActiveCfg = Release|x64
|
||||
{2425F2CC-BB1B-4069-BC10-1C7F535EF8E8}.Release|x64.Build.0 = Release|x64
|
||||
{2425F2CC-BB1B-4069-BC10-1C7F535EF8E8}.Release|x86.ActiveCfg = Release|Win32
|
||||
{2425F2CC-BB1B-4069-BC10-1C7F535EF8E8}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {BB003D83-77D8-4E7B-896D-7C9ADA458F73}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,162 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{2425F2CC-BB1B-4069-BC10-1C7F535EF8E8}</ProjectGuid>
|
||||
<RootNamespace>libatrac9</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
|
||||
<ProjectName>libatrac9</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PreprocessorDefinitions>_WINDLL;COMPILING_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<CompileAs>CompileAsC</CompileAs>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<CompileAs>CompileAsC</CompileAs>
|
||||
<PreprocessorDefinitions>_WINDLL;COMPILING_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<CompileAs>CompileAsC</CompileAs>
|
||||
<PreprocessorDefinitions>_WINDLL;COMPILING_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ExceptionHandling>Sync</ExceptionHandling>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<CompileAs>CompileAsC</CompileAs>
|
||||
<PreprocessorDefinitions>_WINDLL;COMPILING_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="band_extension.h" />
|
||||
<ClInclude Include="bit_allocation.h" />
|
||||
<ClInclude Include="bit_reader.h" />
|
||||
<ClInclude Include="decinit.h" />
|
||||
<ClInclude Include="decoder.h" />
|
||||
<ClInclude Include="error_codes.h" />
|
||||
<ClInclude Include="huffCodes.h" />
|
||||
<ClInclude Include="imdct.h" />
|
||||
<ClInclude Include="libatrac9.h" />
|
||||
<ClInclude Include="quantization.h" />
|
||||
<ClInclude Include="scale_factors.h" />
|
||||
<ClInclude Include="structures.h" />
|
||||
<ClInclude Include="tables.h" />
|
||||
<ClInclude Include="unpack.h" />
|
||||
<ClInclude Include="utility.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="band_extension.c" />
|
||||
<ClCompile Include="bit_allocation.c" />
|
||||
<ClCompile Include="bit_reader.c" />
|
||||
<ClCompile Include="decinit.c" />
|
||||
<ClCompile Include="decoder.c" />
|
||||
<ClCompile Include="huffCodes.c" />
|
||||
<ClCompile Include="imdct.c" />
|
||||
<ClCompile Include="libatrac9.c" />
|
||||
<ClCompile Include="quantization.c" />
|
||||
<ClCompile Include="scale_factors.c" />
|
||||
<ClCompile Include="tables.c" />
|
||||
<ClCompile Include="unpack.c" />
|
||||
<ClCompile Include="utility.c" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -0,0 +1,105 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="libatrac9.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="bit_allocation.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="bit_reader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="decinit.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="decoder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="error_codes.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="huffCodes.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="imdct.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="quantization.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="scale_factors.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="structures.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="tables.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="unpack.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="utility.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="band_extension.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="libatrac9.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="bit_allocation.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="bit_reader.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="decinit.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="decoder.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="huffCodes.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="imdct.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="quantization.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="scale_factors.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="unpack.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="utility.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="band_extension.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="tables.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,54 @@
|
|||
#include "quantization.h"
|
||||
#include <string.h>
|
||||
#include "tables.h"
|
||||
|
||||
void DequantizeSpectra(block* block)
|
||||
{
|
||||
for (int i = 0; i < block->ChannelCount; i++)
|
||||
{
|
||||
channel* channel = &block->Channels[i];
|
||||
memset(channel->Spectra, 0, sizeof(channel->Spectra));
|
||||
|
||||
for (int j = 0; j < channel->CodedQuantUnits; j++)
|
||||
{
|
||||
DequantizeQuantUnit(channel, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DequantizeQuantUnit(channel* channel, int band)
|
||||
{
|
||||
const int subBandIndex = QuantUnitToCoeffIndex[band];
|
||||
const int subBandCount = QuantUnitToCoeffCount[band];
|
||||
const double stepSize = QuantizerStepSize[channel->Precisions[band]];
|
||||
const double stepSizeFine = QuantizerFineStepSize[channel->PrecisionsFine[band]];
|
||||
|
||||
for (int sb = 0; sb < subBandCount; sb++)
|
||||
{
|
||||
const double coarse = channel->QuantizedSpectra[subBandIndex + sb] * stepSize;
|
||||
const double fine = channel->QuantizedSpectraFine[subBandIndex + sb] * stepSizeFine;
|
||||
channel->Spectra[subBandIndex + sb] = coarse + fine;
|
||||
}
|
||||
}
|
||||
|
||||
void ScaleSpectrumBlock(block* block)
|
||||
{
|
||||
for (int i = 0; i < block->ChannelCount; i++)
|
||||
{
|
||||
ScaleSpectrumChannel(&block->Channels[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void ScaleSpectrumChannel(channel* channel)
|
||||
{
|
||||
const int quantUnitCount = channel->Block->QuantizationUnitCount;
|
||||
double* spectra = channel->Spectra;
|
||||
|
||||
for (int i = 0; i < quantUnitCount; i++)
|
||||
{
|
||||
for (int sb = QuantUnitToCoeffIndex[i]; sb < QuantUnitToCoeffIndex[i + 1]; sb++)
|
||||
{
|
||||
spectra[sb] *= SpectrumScale[channel->ScaleFactors[i]];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "structures.h"
|
||||
|
||||
void DequantizeSpectra(block* block);
|
||||
void DequantizeQuantUnit(channel* channel, int band);
|
||||
void ScaleSpectrumBlock(block* block);
|
||||
void ScaleSpectrumChannel(channel* channel);
|
|
@ -0,0 +1,146 @@
|
|||
#include <string.h>
|
||||
#include "huffCodes.h"
|
||||
#include "scale_factors.h"
|
||||
#include "tables.h"
|
||||
#include "utility.h"
|
||||
|
||||
at9_status read_scale_factors(channel * channel, bit_reader_cxt * br)
|
||||
{
|
||||
memset(channel->ScaleFactors, 0, sizeof(channel->ScaleFactors));
|
||||
|
||||
channel->ScaleFactorCodingMode = read_int(br, 2);
|
||||
if (channel->ChannelIndex == 0)
|
||||
{
|
||||
switch (channel->ScaleFactorCodingMode)
|
||||
{
|
||||
case 0:
|
||||
ReadVlcDeltaOffset(channel, br);
|
||||
break;
|
||||
case 1:
|
||||
ReadClcOffset(channel, br);
|
||||
break;
|
||||
case 2:
|
||||
if (channel->Block->FirstInSuperframe) return ERR_UNPACK_SCALE_FACTOR_MODE_INVALID;
|
||||
ReadVlcDistanceToBaseline(channel, br, channel->ScaleFactorsPrev, channel->Block->QuantizationUnitsPrev);
|
||||
break;
|
||||
case 3:
|
||||
if (channel->Block->FirstInSuperframe) return ERR_UNPACK_SCALE_FACTOR_MODE_INVALID;
|
||||
ReadVlcDeltaOffsetWithBaseline(channel, br, channel->ScaleFactorsPrev, channel->Block->QuantizationUnitsPrev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (channel->ScaleFactorCodingMode)
|
||||
{
|
||||
case 0:
|
||||
ReadVlcDeltaOffset(channel, br);
|
||||
break;
|
||||
case 1:
|
||||
ReadVlcDistanceToBaseline(channel, br, channel->Block->Channels[0].ScaleFactors, channel->Block->ExtensionUnit);
|
||||
break;
|
||||
case 2:
|
||||
ReadVlcDeltaOffsetWithBaseline(channel, br, channel->Block->Channels[0].ScaleFactors, channel->Block->ExtensionUnit);
|
||||
break;
|
||||
case 3:
|
||||
if (channel->Block->FirstInSuperframe) return ERR_UNPACK_SCALE_FACTOR_MODE_INVALID;
|
||||
ReadVlcDistanceToBaseline(channel, br, channel->ScaleFactorsPrev, channel->Block->QuantizationUnitsPrev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < channel->Block->ExtensionUnit; i++)
|
||||
{
|
||||
if (channel->ScaleFactors[i] < 0 || channel->ScaleFactors[i] > 31)
|
||||
{
|
||||
return ERR_UNPACK_SCALE_FACTOR_OOB;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(channel->ScaleFactorsPrev, channel->ScaleFactors, sizeof(channel->ScaleFactors));
|
||||
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
void ReadClcOffset(channel* channel, bit_reader_cxt* br)
|
||||
{
|
||||
const int maxBits = 5;
|
||||
int* sf = channel->ScaleFactors;
|
||||
const int bitLength = read_int(br, 2) + 2;
|
||||
const int baseValue = bitLength < maxBits ? read_int(br, maxBits) : 0;
|
||||
|
||||
for (int i = 0; i < channel->Block->ExtensionUnit; i++)
|
||||
{
|
||||
sf[i] = read_int(br, bitLength) + baseValue;
|
||||
}
|
||||
}
|
||||
|
||||
void ReadVlcDeltaOffset(channel* channel, bit_reader_cxt* br)
|
||||
{
|
||||
const int weightIndex = read_int(br, 3);
|
||||
const unsigned char* weights = ScaleFactorWeights[weightIndex];
|
||||
|
||||
int* sf = channel->ScaleFactors;
|
||||
const int baseValue = read_int(br, 5);
|
||||
const int bitLength = read_int(br, 2) + 3;
|
||||
const HuffmanCodebook* codebook = &HuffmanScaleFactorsUnsigned[bitLength];
|
||||
|
||||
sf[0] = read_int(br, bitLength);
|
||||
|
||||
for (int i = 1; i < channel->Block->ExtensionUnit; i++)
|
||||
{
|
||||
const int delta = ReadHuffmanValue(codebook, br, 0);
|
||||
sf[i] = (sf[i - 1] + delta) & (codebook->ValueMax - 1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < channel->Block->ExtensionUnit; i++)
|
||||
{
|
||||
sf[i] += baseValue - weights[i];
|
||||
}
|
||||
}
|
||||
|
||||
void ReadVlcDistanceToBaseline(channel* channel, bit_reader_cxt* br, int* baseline, int baselineLength)
|
||||
{
|
||||
int* sf = channel->ScaleFactors;
|
||||
const int bit_length = read_int(br, 2) + 2;
|
||||
const HuffmanCodebook* codebook = &HuffmanScaleFactorsSigned[bit_length];
|
||||
const int unitCount = min(channel->Block->ExtensionUnit, baselineLength);
|
||||
|
||||
for (int i = 0; i < unitCount; i++)
|
||||
{
|
||||
const int distance = ReadHuffmanValue(codebook, br, TRUE);
|
||||
sf[i] = (baseline[i] + distance) & 31;
|
||||
}
|
||||
|
||||
for (int i = unitCount; i < channel->Block->ExtensionUnit; i++)
|
||||
{
|
||||
sf[i] = read_int(br, 5);
|
||||
}
|
||||
}
|
||||
|
||||
void ReadVlcDeltaOffsetWithBaseline(channel* channel, bit_reader_cxt* br, int* baseline, int baselineLength)
|
||||
{
|
||||
int* sf = channel->ScaleFactors;
|
||||
const int baseValue = read_offset_binary(br, 5);
|
||||
const int bitLength = read_int(br, 2) + 1;
|
||||
const HuffmanCodebook* codebook = &HuffmanScaleFactorsUnsigned[bitLength];
|
||||
const int unitCount = min(channel->Block->ExtensionUnit, baselineLength);
|
||||
|
||||
sf[0] = read_int(br, bitLength);
|
||||
|
||||
for (int i = 1; i < unitCount; i++)
|
||||
{
|
||||
const int delta = ReadHuffmanValue(codebook, br, FALSE);
|
||||
sf[i] = (sf[i - 1] + delta) & (codebook->ValueMax - 1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < unitCount; i++)
|
||||
{
|
||||
sf[i] += baseValue + baseline[i];
|
||||
}
|
||||
|
||||
for (int i = unitCount; i < channel->Block->ExtensionUnit; i++)
|
||||
{
|
||||
sf[i] = read_int(br, 5);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
#include "bit_allocation.h"
|
||||
|
||||
at9_status read_scale_factors(channel* channel, bit_reader_cxt* br);
|
||||
void ReadClcOffset(channel* channel, bit_reader_cxt* br);
|
||||
void ReadVlcDeltaOffset(channel* channel, bit_reader_cxt* br);
|
||||
void ReadVlcDistanceToBaseline(channel* channel, bit_reader_cxt* br, int* baseline, int baselineLength);
|
||||
void ReadVlcDeltaOffsetWithBaseline(channel* channel, bit_reader_cxt* br, int* baseline, int baselineLength);
|
|
@ -0,0 +1,160 @@
|
|||
#pragma once
|
||||
|
||||
#define CONFIG_DATA_SIZE 4
|
||||
#define MAX_BLOCK_COUNT 5
|
||||
#define MAX_BLOCK_CHANNEL_COUNT 2
|
||||
#define MAX_FRAME_SAMPLES 256
|
||||
#define MAX_BEX_VALUES 4
|
||||
|
||||
#define MAX_QUANT_UNITS 30
|
||||
|
||||
typedef struct frame frame;
|
||||
typedef struct block block;
|
||||
|
||||
typedef enum BlockType {
|
||||
Mono = 0,
|
||||
Stereo = 1,
|
||||
LFE = 2
|
||||
} BlockType;
|
||||
|
||||
typedef struct {
|
||||
int BlockCount;
|
||||
int ChannelCount;
|
||||
enum BlockType Types[MAX_BLOCK_COUNT];
|
||||
} ChannelConfig;
|
||||
|
||||
typedef struct {
|
||||
unsigned char ConfigData[CONFIG_DATA_SIZE];
|
||||
int SampleRateIndex;
|
||||
int ChannelConfigIndex;
|
||||
int FrameBytes;
|
||||
int SuperframeIndex;
|
||||
|
||||
ChannelConfig ChannelConfig;
|
||||
int ChannelCount;
|
||||
int SampleRate;
|
||||
int HighSampleRate;
|
||||
int FramesPerSuperframe;
|
||||
int FrameSamplesPower;
|
||||
int FrameSamples;
|
||||
int SuperframeBytes;
|
||||
int SuperframeSamples;
|
||||
} ConfigData;
|
||||
|
||||
typedef struct {
|
||||
int initialized;
|
||||
unsigned short stateA;
|
||||
unsigned short stateB;
|
||||
unsigned short stateC;
|
||||
unsigned short stateD;
|
||||
} rng_cxt;
|
||||
|
||||
typedef struct {
|
||||
int bits;
|
||||
int size;
|
||||
double scale;
|
||||
double _imdctPrevious[MAX_FRAME_SAMPLES];
|
||||
double* window;
|
||||
double* sinTable;
|
||||
double* cosTable;
|
||||
} mdct;
|
||||
|
||||
typedef struct {
|
||||
frame* Frame;
|
||||
block* Block;
|
||||
ConfigData* config;
|
||||
int ChannelIndex;
|
||||
|
||||
mdct mdct;
|
||||
|
||||
double Pcm[MAX_FRAME_SAMPLES];
|
||||
double Spectra[MAX_FRAME_SAMPLES];
|
||||
|
||||
int CodedQuantUnits;
|
||||
int ScaleFactorCodingMode;
|
||||
|
||||
int ScaleFactors[31];
|
||||
int ScaleFactorsPrev[31];
|
||||
|
||||
int Precisions[MAX_QUANT_UNITS];
|
||||
int PrecisionsFine[MAX_QUANT_UNITS];
|
||||
int PrecisionMask[MAX_QUANT_UNITS];
|
||||
|
||||
int CodebookSet[MAX_QUANT_UNITS];
|
||||
|
||||
int QuantizedSpectra[MAX_FRAME_SAMPLES];
|
||||
int QuantizedSpectraFine[MAX_FRAME_SAMPLES];
|
||||
|
||||
int BexMode;
|
||||
int BexValueCount;
|
||||
int BexValues[MAX_BEX_VALUES];
|
||||
|
||||
rng_cxt rng;
|
||||
} channel;
|
||||
|
||||
struct block {
|
||||
frame* Frame;
|
||||
ConfigData* config;
|
||||
enum BlockType BlockType;
|
||||
int BlockIndex;
|
||||
channel Channels[MAX_BLOCK_CHANNEL_COUNT];
|
||||
int ChannelCount;
|
||||
int FirstInSuperframe;
|
||||
int ReuseBandParams;
|
||||
|
||||
int BandCount;
|
||||
int StereoBand;
|
||||
int ExtensionBand;
|
||||
int QuantizationUnitCount;
|
||||
int StereoQuantizationUnit;
|
||||
int ExtensionUnit;
|
||||
int QuantizationUnitsPrev;
|
||||
|
||||
int Gradient[31];
|
||||
int GradientMode;
|
||||
int GradientStartUnit;
|
||||
int GradientStartValue;
|
||||
int GradientEndUnit;
|
||||
int GradientEndValue;
|
||||
int GradientBoundary;
|
||||
|
||||
int PrimaryChannelIndex;
|
||||
int HasJointStereoSigns;
|
||||
int JointStereoSigns[MAX_QUANT_UNITS];
|
||||
|
||||
int BandExtensionEnabled;
|
||||
int HasExtensionData;
|
||||
int BexDataLength;
|
||||
int BexMode;
|
||||
};
|
||||
|
||||
struct frame {
|
||||
ConfigData* config;
|
||||
int FrameIndex;
|
||||
block Blocks[MAX_BLOCK_COUNT];
|
||||
int frameNum;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int initialized;
|
||||
int wlength;
|
||||
ConfigData config;
|
||||
frame frame;
|
||||
} atrac9_handle;
|
||||
|
||||
typedef struct {
|
||||
int group_b_unit;
|
||||
int group_c_unit;
|
||||
int band_count;
|
||||
} bex_group;
|
||||
|
||||
typedef struct {
|
||||
int channels;
|
||||
int channelConfigIndex;
|
||||
int samplingRate;
|
||||
int superframeSize;
|
||||
int framesInSuperframe;
|
||||
int frameSamples;
|
||||
int wlength;
|
||||
unsigned char configData[CONFIG_DATA_SIZE];
|
||||
} CodecInfo;
|
|
@ -0,0 +1,7 @@
|
|||
#include "tables.h"
|
||||
|
||||
double MdctWindow[3][256];
|
||||
double ImdctWindow[3][256];
|
||||
double SinTables[9][256];
|
||||
double CosTables[9][256];
|
||||
int ShuffleTables[9][256];
|
|
@ -0,0 +1,389 @@
|
|||
#pragma once
|
||||
|
||||
#include "structures.h"
|
||||
|
||||
static const ChannelConfig ChannelConfigs[6] =
|
||||
{
|
||||
{1, 1, Mono},
|
||||
{2, 2, Mono, Mono},
|
||||
{1, 2, Stereo},
|
||||
{4, 6, Stereo, Mono, LFE, Stereo},
|
||||
{5, 8, Stereo, Mono, LFE, Stereo, Stereo},
|
||||
{2, 4, Stereo, Stereo},
|
||||
};
|
||||
|
||||
static const int MaxHuffPrecision[2] = { 7, 1 };
|
||||
static const int MinBandCount[2] = { 3, 1 };
|
||||
static const int MaxExtensionBand[2] = { 18, 16 };
|
||||
|
||||
static const int SamplingRateIndexToFrameSamplesPower[16] =
|
||||
{ 6, 6, 7, 7, 7, 8, 8, 8, 6, 6, 7, 7, 7, 8, 8, 8 };
|
||||
|
||||
static const int MaxBandCount[16] =
|
||||
{ 8, 8, 12, 12, 12, 18, 18, 18, 8, 8, 12, 12, 12, 16, 16, 16 };
|
||||
|
||||
static const int BandToQuantUnitCount[19] =
|
||||
{ 0, 4, 8, 10, 12, 13, 14, 15, 16, 18, 20, 21, 22, 23, 24, 25, 26, 28, 30 };
|
||||
|
||||
static const int QuantUnitToCoeffCount[30] =
|
||||
{
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 8, 8, 8,
|
||||
8, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16
|
||||
};
|
||||
|
||||
static const int QuantUnitToCoeffIndex[31] =
|
||||
{
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
|
||||
64, 72, 80, 88, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256
|
||||
};
|
||||
|
||||
static const int QuantUnitToCodebookIndex[30] =
|
||||
{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3
|
||||
};
|
||||
|
||||
static const int SampleRates[16] =
|
||||
{
|
||||
11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
|
||||
44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000
|
||||
};
|
||||
|
||||
static const unsigned char GradientCurves[48][48] = {
|
||||
{ 1 },
|
||||
{ 1, 16 },
|
||||
{ 1, 7, 25 },
|
||||
{ 1, 4, 16, 27 },
|
||||
{ 1, 3, 10, 21, 28 },
|
||||
{ 1, 3, 7, 16, 25, 29 },
|
||||
{ 1, 2, 5, 11, 20, 26, 29 },
|
||||
{ 1, 2, 4, 9, 16, 23, 27, 29 },
|
||||
{ 1, 2, 3, 7, 12, 19, 25, 28, 29 },
|
||||
{ 1, 2, 3, 5, 10, 16, 21, 26, 28, 29 },
|
||||
{ 1, 2, 3, 5, 8, 12, 19, 23, 26, 28, 29 },
|
||||
{ 1, 2, 3, 4, 7, 11, 16, 21, 25, 27, 29, 30 },
|
||||
{ 1, 1, 2, 4, 5, 9, 13, 18, 22, 26, 27, 29, 30 },
|
||||
{ 1, 1, 2, 3, 5, 8, 11, 16, 20, 23, 26, 28, 29, 30 },
|
||||
{ 1, 1, 2, 3, 4, 7, 10, 13, 18, 21, 25, 27, 28, 29, 30 },
|
||||
{ 1, 1, 2, 3, 4, 6, 9, 12, 16, 20, 23, 26, 27, 28, 29, 30 },
|
||||
{ 1, 1, 2, 3, 4, 5, 7, 10, 13, 18, 21, 24, 26, 27, 28, 29, 30 },
|
||||
{ 1, 1, 2, 3, 3, 5, 7, 9, 12, 16, 19, 22, 25, 26, 28, 29, 29, 30 },
|
||||
{ 1, 1, 2, 2, 3, 4, 6, 8, 11, 13, 18, 20, 23, 25, 27, 28, 29, 29, 30 },
|
||||
{ 1, 1, 2, 2, 3, 4, 5, 7, 10, 12, 16, 19, 21, 24, 26, 27, 28, 29, 29, 30 },
|
||||
{ 1, 1, 2, 2, 3, 4, 5, 7, 9, 11, 13, 18, 20, 22, 25, 26, 27, 28, 29, 29, 30 },
|
||||
{ 1, 1, 2, 2, 3, 3, 5, 6, 8, 10, 12, 16, 19, 21, 23, 25, 26, 28, 28, 29, 29,
|
||||
30 },
|
||||
{ 1, 1, 2, 2, 3, 3, 4, 5, 7, 9, 11, 13, 18, 20, 22, 24, 26, 27, 28, 28, 29,
|
||||
29, 30 },
|
||||
{ 1, 1, 2, 2, 3, 3, 4, 5, 7, 9, 11, 13, 16, 19, 21, 23, 25, 26, 27, 28, 29,
|
||||
29, 30, 30 },
|
||||
{ 1, 1, 1, 2, 2, 3, 4, 5, 6, 8, 10, 12, 15, 16, 19, 21, 23, 25, 26, 27, 28,
|
||||
29, 29, 30, 30 },
|
||||
{ 1, 1, 1, 2, 2, 3, 4, 4, 5, 7, 9, 11, 13, 16, 18, 20, 22, 24, 26, 27, 27,
|
||||
28, 29, 29, 30, 30 },
|
||||
{ 1, 1, 1, 2, 2, 3, 3, 4, 5, 7, 8, 10, 12, 15, 16, 19, 21, 23, 25, 26, 27,
|
||||
28, 28, 29, 29, 30, 30 },
|
||||
{ 1, 1, 1, 2, 2, 3, 3, 4, 5, 6, 8, 9, 11, 13, 16, 18, 20, 22, 23, 25, 26, 27,
|
||||
28, 28, 29, 29, 30, 30 },
|
||||
{ 1, 1, 1, 2, 2, 3, 3, 4, 5, 5, 7, 9, 10, 12, 15, 16, 19, 21, 22, 24, 26, 26,
|
||||
27, 28, 28, 29, 29, 30, 30 },
|
||||
{ 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 7, 8, 10, 11, 13, 16, 18, 20, 21, 23, 25, 26,
|
||||
27, 27, 28, 29, 29, 29, 30, 30 },
|
||||
{ 1, 1, 1, 2, 2, 2, 3, 3, 4, 5, 6, 8, 9, 11, 12, 15, 16, 19, 20, 22, 23, 25,
|
||||
26, 27, 28, 28, 29, 29, 29, 30, 30 },
|
||||
{ 1, 1, 1, 2, 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 13, 16, 18, 20, 21, 23, 24,
|
||||
26, 26, 27, 28, 28, 29, 29, 29, 30, 30 },
|
||||
{ 1, 1, 1, 2, 2, 2, 3, 3, 4, 5, 5, 7, 8, 9, 11, 12, 15, 16, 19, 20, 22, 23,
|
||||
25, 26, 26, 27, 28, 28, 29, 29, 29, 30, 30 },
|
||||
{ 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 6, 7, 9, 10, 12, 13, 16, 18, 19, 21, 22,
|
||||
24, 25, 26, 27, 27, 28, 28, 29, 29, 29, 30, 30 },
|
||||
{ 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 5, 6, 7, 8, 10, 11, 12, 15, 16, 19, 20, 21,
|
||||
23, 24, 25, 26, 27, 28, 28, 28, 29, 29, 29, 30, 30 },
|
||||
{ 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 5, 5, 7, 8, 9, 11, 12, 13, 16, 18, 19, 21,
|
||||
22, 23, 25, 26, 26, 27, 28, 28, 29, 29, 29, 30, 30, 30 },
|
||||
{ 1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 6, 7, 9, 10, 11, 13, 15, 16, 18, 20,
|
||||
21, 22, 24, 25, 26, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30 },
|
||||
{ 1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 9, 11, 12, 13, 16, 18, 19, 20,
|
||||
22, 23, 24, 25, 26, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30 },
|
||||
{ 1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 7, 8, 9, 10, 11, 13, 15, 16, 18, 20,
|
||||
21, 22, 23, 25, 26, 26, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30 },
|
||||
{ 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 5, 5, 6, 7, 9, 10, 11, 12, 13, 16, 18, 19,
|
||||
20, 21, 23, 24, 25, 26, 26, 27, 28, 28, 28, 29, 29, 29, 30, 30, 30 },
|
||||
{ 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 6, 7, 8, 9, 10, 12, 13, 15, 16, 18,
|
||||
19, 21, 22, 23, 24, 25, 26, 27, 27, 28, 28, 28, 29, 29, 29, 30, 30, 30 },
|
||||
{ 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 7, 8, 9, 10, 11, 12, 13, 16, 18,
|
||||
19, 20, 21, 22, 23, 25, 26, 26, 27, 27, 28, 28, 29, 29, 29, 29, 30, 30,
|
||||
30 },
|
||||
{ 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16,
|
||||
18, 19, 20, 22, 23, 24, 25, 26, 26, 27, 27, 28, 28, 29, 29, 29, 29, 30, 30,
|
||||
30 },
|
||||
{ 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 5, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16,
|
||||
18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, 28, 28, 28, 29, 29, 29, 29, 30,
|
||||
30, 30 },
|
||||
{ 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 7, 8, 9, 10, 11, 12, 13, 15,
|
||||
16, 18, 19, 20, 21, 22, 23, 25, 26, 26, 27, 27, 28, 28, 28, 29, 29, 29, 29,
|
||||
30, 30, 30 },
|
||||
{ 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 11, 12, 13,
|
||||
16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, 27, 28, 28, 28, 29, 29, 29,
|
||||
29, 30, 30, 30 },
|
||||
{ 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 11, 12, 13,
|
||||
15, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, 27, 28, 28, 28, 29, 29,
|
||||
29, 29, 30, 30, 30 },
|
||||
{ 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 11, 12, 13,
|
||||
15, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, 27, 28, 28, 28, 29, 29,
|
||||
29, 29, 30, 30, 30, 30 }
|
||||
};
|
||||
|
||||
static const unsigned char ScaleFactorWeights[8][32] = {
|
||||
{ 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 3, 2, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 6, 6,
|
||||
7, 7, 8, 10, 12, 12, 12 },
|
||||
{ 3, 2, 2, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 2, 3,
|
||||
3, 4, 5, 7, 10, 10, 10 },
|
||||
{ 0, 2, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7,
|
||||
7, 7, 8, 9, 12, 12, 12 },
|
||||
{ 0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 8, 8, 10,
|
||||
11, 11, 12, 13, 13, 13, 13 },
|
||||
{ 0, 2, 2, 3, 3, 4, 4, 5, 4, 5, 5, 5, 5, 6, 7, 8, 8, 8, 8, 9, 9, 9, 10, 10,
|
||||
11, 12, 12, 13, 13, 14, 14, 14 },
|
||||
{ 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5,
|
||||
6, 7, 7, 9, 11, 11, 11 },
|
||||
{ 0, 5, 8, 10, 11, 11, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
|
||||
13, 13, 13, 13, 13, 12, 12, 12, 12, 13, 15, 15, 15 },
|
||||
{ 0, 2, 3, 4, 5, 6, 6, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 11, 11, 11, 11, 11,
|
||||
11, 12, 12, 12, 12, 13, 13, 15, 15, 15 }
|
||||
};
|
||||
|
||||
static const double SpectrumScale[32] =
|
||||
{
|
||||
3.0517578125e-5, 6.1035156250e-5, 1.2207031250e-4, 2.4414062500e-4,
|
||||
4.8828125000e-4, 9.7656250000e-4, 1.9531250000e-3, 3.9062500000e-3,
|
||||
7.8125000000e-3, 1.5625000000e-2, 3.1250000000e-2, 6.2500000000e-2,
|
||||
1.2500000000e-1, 2.5000000000e-1, 5.0000000000e-1, 1.0000000000e+0,
|
||||
2.0000000000e+0, 4.0000000000e+0, 8.0000000000e+0, 1.6000000000e+1,
|
||||
3.2000000000e+1, 6.4000000000e+1, 1.2800000000e+2, 2.5600000000e+2,
|
||||
5.1200000000e+2, 1.0240000000e+3, 2.0480000000e+3, 4.0960000000e+3,
|
||||
8.1920000000e+3, 1.6384000000e+4, 3.2768000000e+4, 6.5536000000e+4
|
||||
};
|
||||
|
||||
static const double QuantizerInverseStepSize[16] =
|
||||
{
|
||||
0.5, 1.5, 3.5, 7.5, 15.5, 31.5, 63.5, 127.5,
|
||||
255.5, 511.5, 1023.5, 2047.5, 4095.5, 8191.5, 16383.5, 32767.5
|
||||
};
|
||||
|
||||
static const double QuantizerStepSize[16] =
|
||||
{
|
||||
2.0000000000000000e+0, 6.6666666666666663e-1, 2.8571428571428570e-1, 1.3333333333333333e-1,
|
||||
6.4516129032258063e-2, 3.1746031746031744e-2, 1.5748031496062992e-2, 7.8431372549019607e-3,
|
||||
3.9138943248532287e-3, 1.9550342130987292e-3, 9.7703957010258913e-4, 4.8840048840048840e-4,
|
||||
2.4417043096081065e-4, 1.2207776353537203e-4, 6.1037018951994385e-5, 3.0518043793392844e-5
|
||||
};
|
||||
|
||||
static const double QuantizerFineStepSize[16] =
|
||||
{
|
||||
3.0518043793392844e-05, 1.0172681264464281e-05, 4.3597205419132631e-06, 2.0345362528928561e-06,
|
||||
9.8445302559331759e-07, 4.8441339354591809e-07, 2.4029955742829012e-07, 1.1967860311134448e-07,
|
||||
5.9722199204291275e-08, 2.9831909866464167e-08, 1.4908668194134265e-08, 7.4525137468602791e-09,
|
||||
3.7258019525568114e-09, 1.8627872668859698e-09, 9.3136520869755679e-10, 4.6567549848772173e-10
|
||||
};
|
||||
|
||||
static const bex_group BexGroupInfo[8] =
|
||||
{
|
||||
{ 16, 21, 0 },
|
||||
{ 18, 22, 1 },
|
||||
{ 20, 22, 2 },
|
||||
{ 21, 22, 3 },
|
||||
{ 21, 22, 3 },
|
||||
{ 23, 24, 4 },
|
||||
{ 23, 24, 4 },
|
||||
{ 24, 24, 5 }
|
||||
};
|
||||
|
||||
static const int BexEncodedValueCounts[5][6] =
|
||||
{
|
||||
{0, 0, 0, 4, 4, 2},
|
||||
{0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 2, 2, 1},
|
||||
{0, 0, 0, 2, 2, 2},
|
||||
{1, 1, 1, 0, 0, 0}
|
||||
};
|
||||
|
||||
// [mode][bands][valueIndex]
|
||||
static const int BexDataLengths[5][6][4] =
|
||||
{
|
||||
{
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{5, 4, 3, 3},
|
||||
{4, 4, 3, 4},
|
||||
{4, 5, 0, 0}
|
||||
}, {
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0}
|
||||
}, {
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{6, 6, 0, 0},
|
||||
{6, 6, 0, 0},
|
||||
{6, 0, 0, 0}
|
||||
}, {
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{4, 4, 0, 0},
|
||||
{4, 4, 0, 0},
|
||||
{4, 4, 0, 0}
|
||||
}, {
|
||||
{3, 0, 0, 0},
|
||||
{3, 0, 0, 0},
|
||||
{3, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0}
|
||||
}
|
||||
};
|
||||
|
||||
static const double BexMode0Bands3[5][32] =
|
||||
{
|
||||
{
|
||||
0.000000e+0, 1.988220e-1, 2.514343e-1, 2.960510e-1,
|
||||
3.263550e-1, 3.771362e-1, 3.786926e-1, 4.540405e-1,
|
||||
4.877625e-1, 5.262451e-1, 5.447083e-1, 5.737000e-1,
|
||||
6.212158e-1, 6.222839e-1, 6.560974e-1, 6.896667e-1,
|
||||
7.555542e-1, 7.677917e-1, 7.918091e-1, 7.971497e-1,
|
||||
8.188171e-1, 8.446045e-1, 9.790649e-1, 9.822083e-1,
|
||||
9.846191e-1, 9.859314e-1, 9.863586e-1, 9.863892e-1,
|
||||
9.873352e-1, 9.881287e-1, 9.898682e-1, 9.913330e-1
|
||||
}, {
|
||||
0.000000e+0, 9.982910e-1, 7.592773e-2, 7.179565e-1,
|
||||
9.851379e-1, 5.340271e-1, 9.013672e-1, 6.349182e-1,
|
||||
7.226257e-1, 1.948547e-1, 7.628174e-1, 9.873657e-1,
|
||||
8.112183e-1, 2.715454e-1, 9.734192e-1, 1.443787e-1,
|
||||
4.640198e-1, 3.249207e-1, 3.790894e-1, 8.276367e-2,
|
||||
5.954590e-1, 2.864380e-1, 9.806824e-1, 7.929077e-1,
|
||||
6.292114e-1, 4.887085e-1, 2.905273e-1, 1.301880e-1,
|
||||
3.140869e-1, 5.482483e-1, 4.210815e-1, 1.182861e-1
|
||||
}, {
|
||||
0.000000e+0, 3.155518e-2, 8.581543e-2, 1.364746e-1,
|
||||
1.858826e-1, 2.368469e-1, 2.888184e-1, 3.432617e-1,
|
||||
4.012451e-1, 4.623108e-1, 5.271301e-1, 5.954895e-1,
|
||||
6.681213e-1, 7.448425e-1, 8.245239e-1, 9.097290e-1
|
||||
}, {
|
||||
0.000000e+0, 4.418945e-2, 1.303711e-1, 2.273560e-1,
|
||||
3.395996e-1, 4.735718e-1, 6.267090e-1, 8.003845e-1
|
||||
}, {
|
||||
0.000000e+0, 2.804565e-2, 9.683228e-2, 1.849976e-1,
|
||||
3.005981e-1, 4.470520e-1, 6.168518e-1, 8.007813e-1
|
||||
}
|
||||
};
|
||||
|
||||
static const double BexMode0Bands4[5][16] =
|
||||
{
|
||||
{
|
||||
0.000000e+0, 2.708740e-1, 3.479614e-1, 3.578186e-1,
|
||||
5.083618e-1, 5.299072e-1, 5.819092e-1, 6.381836e-1,
|
||||
7.276917e-1, 7.595520e-1, 7.878723e-1, 9.707336e-1,
|
||||
9.713135e-1, 9.736023e-1, 9.759827e-1, 9.832458e-1
|
||||
}, {
|
||||
0.000000e+0, 2.330627e-1, 5.891418e-1, 7.170410e-1,
|
||||
2.036438e-1, 1.613464e-1, 6.668701e-1, 9.481201e-1,
|
||||
9.769897e-1, 5.111694e-1, 3.522644e-1, 8.209534e-1,
|
||||
2.933960e-1, 9.757690e-1, 5.289917e-1, 4.372253e-1
|
||||
}, {
|
||||
0.000000e+0, 4.360962e-2, 1.056519e-1, 1.590576e-1,
|
||||
2.078857e-1, 2.572937e-1, 3.082581e-1, 3.616028e-1,
|
||||
4.191589e-1, 4.792175e-1, 5.438538e-1, 6.125183e-1,
|
||||
6.841125e-1, 7.589417e-1, 8.365173e-1, 9.148254e-1
|
||||
}, {
|
||||
0.000000e+0, 4.074097e-2, 1.164551e-1, 2.077026e-1,
|
||||
3.184509e-1, 4.532166e-1, 6.124268e-1, 7.932129e-1
|
||||
}, {
|
||||
0.000000e+0, 8.880615e-3, 2.932739e-2, 5.593872e-2,
|
||||
8.825684e-2, 1.259155e-1, 1.721497e-1, 2.270813e-1,
|
||||
2.901611e-1, 3.579712e-1, 4.334106e-1, 5.147095e-1,
|
||||
6.023254e-1, 6.956177e-1, 7.952881e-1, 8.977356e-1
|
||||
}
|
||||
};
|
||||
|
||||
static const double BexMode0Bands5[3][32] =
|
||||
{
|
||||
{
|
||||
0.000000e+0, 7.379150e-2, 1.806335e-1, 2.687073e-1,
|
||||
3.407898e-1, 4.047546e-1, 4.621887e-1, 5.168762e-1,
|
||||
5.703125e-1, 6.237488e-1, 6.763611e-1, 7.288208e-1,
|
||||
7.808533e-1, 8.337708e-1, 8.874512e-1, 9.418030e-1
|
||||
}, {
|
||||
0.000000e+0, 7.980347e-2, 1.615295e-1, 1.665649e-1,
|
||||
1.822205e-1, 2.185669e-1, 2.292175e-1, 2.456665e-1,
|
||||
2.666321e-1, 3.306580e-1, 3.330688e-1, 3.765259e-1,
|
||||
4.085083e-1, 4.400024e-1, 4.407654e-1, 4.817505e-1,
|
||||
4.924011e-1, 5.320740e-1, 5.893860e-1, 6.131287e-1,
|
||||
6.212463e-1, 6.278076e-1, 6.308899e-1, 7.660828e-1,
|
||||
7.850647e-1, 7.910461e-1, 7.929382e-1, 8.038330e-1,
|
||||
9.834900e-1, 9.846191e-1, 9.852295e-1, 9.862671e-1
|
||||
}, {
|
||||
0.000000e+0, 6.084290e-1, 3.672791e-1, 3.151855e-1,
|
||||
1.488953e-1, 2.571716e-1, 5.103455e-1, 3.311157e-1,
|
||||
5.426025e-2, 4.254456e-1, 7.998352e-1, 7.873230e-1,
|
||||
5.418701e-1, 2.925110e-1, 8.468628e-2, 1.410522e-1,
|
||||
9.819641e-1, 9.609070e-1, 3.530884e-2, 9.729004e-2,
|
||||
5.758362e-1, 9.941711e-1, 7.215576e-1, 7.183228e-1,
|
||||
2.028809e-1, 9.588623e-2, 2.032166e-1, 1.338806e-1,
|
||||
5.003357e-1, 1.874390e-1, 9.804993e-1, 1.107788e-1
|
||||
}
|
||||
};
|
||||
|
||||
static const double BexMode2Scale[64] =
|
||||
{
|
||||
4.272461e-4, 1.312256e-3, 2.441406e-3, 3.692627e-3,
|
||||
4.913330e-3, 6.134033e-3, 7.507324e-3, 8.972168e-3,
|
||||
1.049805e-2, 1.223755e-2, 1.406860e-2, 1.599121e-2,
|
||||
1.800537e-2, 2.026367e-2, 2.264404e-2, 2.517700e-2,
|
||||
2.792358e-2, 3.073120e-2, 3.344727e-2, 3.631592e-2,
|
||||
3.952026e-2, 4.275513e-2, 4.608154e-2, 4.968262e-2,
|
||||
5.355835e-2, 5.783081e-2, 6.195068e-2, 6.677246e-2,
|
||||
7.196045e-2, 7.745361e-2, 8.319092e-2, 8.993530e-2,
|
||||
9.759521e-2, 1.056213e-1, 1.138916e-1, 1.236267e-1,
|
||||
1.348267e-1, 1.470337e-1, 1.603394e-1, 1.755676e-1,
|
||||
1.905823e-1, 2.071228e-1, 2.245178e-1, 2.444153e-1,
|
||||
2.658997e-1, 2.897644e-1, 3.146057e-1, 3.450012e-1,
|
||||
3.766174e-1, 4.122620e-1, 4.505615e-1, 4.893799e-1,
|
||||
5.305481e-1, 5.731201e-1, 6.157837e-1, 6.580811e-1,
|
||||
6.985168e-1, 7.435303e-1, 7.865906e-1, 8.302612e-1,
|
||||
8.718567e-1, 9.125671e-1, 9.575806e-1, 9.996643e-1
|
||||
};
|
||||
|
||||
static const double BexMode3Initial[16] =
|
||||
{
|
||||
3.491211e-1, 5.371094e-1, 6.782227e-1, 7.910156e-1,
|
||||
9.057617e-1, 1.024902e+0, 1.156250e+0, 1.290527e+0,
|
||||
1.458984e+0, 1.664551e+0, 1.929688e+0, 2.278320e+0,
|
||||
2.831543e+0, 3.659180e+0, 5.257813e+0, 8.373047e+0
|
||||
};
|
||||
|
||||
static const double BexMode3Rate[16] =
|
||||
{
|
||||
-2.913818e-1, -2.541504e-1, -1.664429e-1, -1.476440e-1,
|
||||
-1.342163e-1, -1.220703e-1, -1.117554e-1, -1.026611e-1,
|
||||
-9.436035e-2, -8.483887e-2, -7.476807e-2, -6.304932e-2,
|
||||
-4.492188e-2, -2.447510e-2, +1.831055e-4, +4.174805e-2
|
||||
};
|
||||
|
||||
static const double BexMode4Multiplier[8] =
|
||||
{
|
||||
3.610229e-2, 1.260681e-1, 2.227478e-1, 3.338318e-1,
|
||||
4.662170e-1, 6.221313e-1, 7.989197e-1, 9.939575e-1
|
||||
};
|
||||
|
||||
extern double MdctWindow[3][256];
|
||||
extern double ImdctWindow[3][256];
|
||||
extern double SinTables[9][256];
|
||||
extern double CosTables[9][256];
|
||||
extern int ShuffleTables[9][256];
|
||||
|
|
@ -0,0 +1,423 @@
|
|||
#include "tables.h"
|
||||
#include "unpack.h"
|
||||
#include "bit_allocation.h"
|
||||
#include <string.h>
|
||||
#include "scale_factors.h"
|
||||
#include "utility.h"
|
||||
#include "huffCodes.h"
|
||||
|
||||
at9_status UnpackFrame(frame* frame, bit_reader_cxt* br)
|
||||
{
|
||||
const int block_count = frame->config->ChannelConfig.BlockCount;
|
||||
|
||||
for (int i = 0; i < block_count; i++)
|
||||
{
|
||||
ERROR_CHECK(UnpackBlock(&frame->Blocks[i], br));
|
||||
}
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
at9_status UnpackBlock(block* block, bit_reader_cxt* br)
|
||||
{
|
||||
ERROR_CHECK(ReadBlockHeader(block, br));
|
||||
|
||||
if (block->BlockType == LFE)
|
||||
{
|
||||
ERROR_CHECK(UnpackLfeBlock(block, br));
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_CHECK(UnpackStandardBlock(block, br));
|
||||
}
|
||||
|
||||
align_position(br, 8);
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
at9_status ReadBlockHeader(block* block, bit_reader_cxt* br)
|
||||
{
|
||||
int firstInSuperframe = block->Frame->FrameIndex == 0;
|
||||
block->FirstInSuperframe = !read_int(br, 1);
|
||||
block->ReuseBandParams = read_int(br, 1);
|
||||
|
||||
if (block->FirstInSuperframe && block->ReuseBandParams && block->BlockType != LFE)
|
||||
{
|
||||
return ERR_UNPACK_REUSE_BAND_PARAMS_INVALID;
|
||||
}
|
||||
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
at9_status UnpackStandardBlock(block* block, bit_reader_cxt* br)
|
||||
{
|
||||
if (!block->ReuseBandParams)
|
||||
{
|
||||
ERROR_CHECK(ReadBandParams(block, br));
|
||||
}
|
||||
|
||||
ERROR_CHECK(ReadGradientParams(block, br));
|
||||
ERROR_CHECK(CreateGradient(block));
|
||||
ERROR_CHECK(ReadStereoParams(block, br));
|
||||
ERROR_CHECK(ReadExtensionParams(block, br));
|
||||
|
||||
for (int i = 0; i < block->ChannelCount; i++)
|
||||
{
|
||||
channel* channel = &block->Channels[i];
|
||||
UpdateCodedUnits(channel);
|
||||
|
||||
ERROR_CHECK(read_scale_factors(channel, br));
|
||||
CalculateMask(channel);
|
||||
CalculatePrecisions(channel);
|
||||
CalculateSpectrumCodebookIndex(channel);
|
||||
|
||||
ERROR_CHECK(ReadSpectra(channel, br));
|
||||
ERROR_CHECK(ReadSpectraFine(channel, br));
|
||||
}
|
||||
|
||||
block->QuantizationUnitsPrev = block->BandExtensionEnabled ? block->ExtensionUnit : block->QuantizationUnitCount;
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
at9_status ReadBandParams(block* block, bit_reader_cxt* br)
|
||||
{
|
||||
const int minBandCount = MinBandCount[block->config->HighSampleRate];
|
||||
const int maxExtensionBand = MaxExtensionBand[block->config->HighSampleRate];
|
||||
block->BandCount = read_int(br, 4) + minBandCount;
|
||||
block->QuantizationUnitCount = BandToQuantUnitCount[block->BandCount];
|
||||
|
||||
if (block->BandCount < minBandCount || block->BandCount >
|
||||
MaxBandCount[block->config->SampleRateIndex])
|
||||
{
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
if (block->BlockType == Stereo)
|
||||
{
|
||||
block->StereoBand = read_int(br, 4);
|
||||
block->StereoBand += minBandCount;
|
||||
block->StereoQuantizationUnit = BandToQuantUnitCount[block->StereoBand];
|
||||
}
|
||||
else
|
||||
{
|
||||
block->StereoBand = block->BandCount;
|
||||
}
|
||||
|
||||
block->BandExtensionEnabled = read_int(br, 1);
|
||||
if (block->BandExtensionEnabled)
|
||||
{
|
||||
block->ExtensionBand = read_int(br, 4);
|
||||
block->ExtensionBand += minBandCount;
|
||||
|
||||
if (block->ExtensionBand < block->BandCount || block->ExtensionBand > maxExtensionBand)
|
||||
{
|
||||
return ERR_UNPACK_BAND_PARAMS_INVALID;
|
||||
}
|
||||
|
||||
block->ExtensionUnit = BandToQuantUnitCount[block->ExtensionBand];
|
||||
}
|
||||
else
|
||||
{
|
||||
block->ExtensionBand = block->BandCount;
|
||||
block->ExtensionUnit = block->QuantizationUnitCount;
|
||||
}
|
||||
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
at9_status ReadGradientParams(block* block, bit_reader_cxt* br)
|
||||
{
|
||||
block->GradientMode = read_int(br, 2);
|
||||
if (block->GradientMode > 0)
|
||||
{
|
||||
block->GradientEndUnit = 31;
|
||||
block->GradientEndValue = 31;
|
||||
block->GradientStartUnit = read_int(br, 5);
|
||||
block->GradientStartValue = read_int(br, 5);
|
||||
}
|
||||
else
|
||||
{
|
||||
block->GradientStartUnit = read_int(br, 6);
|
||||
block->GradientEndUnit = read_int(br, 6) + 1;
|
||||
block->GradientStartValue = read_int(br, 5);
|
||||
block->GradientEndValue = read_int(br, 5);
|
||||
}
|
||||
block->GradientBoundary = read_int(br, 4);
|
||||
|
||||
if (block->GradientBoundary > block->QuantizationUnitCount)
|
||||
{
|
||||
return ERR_UNPACK_GRAD_BOUNDARY_INVALID;
|
||||
}
|
||||
if (block->GradientStartUnit < 1 || block->GradientStartUnit >= 48)
|
||||
{
|
||||
return ERR_UNPACK_GRAD_START_UNIT_OOB;
|
||||
}
|
||||
if (block->GradientEndUnit < 1 || block->GradientEndUnit >= 48)
|
||||
{
|
||||
return ERR_UNPACK_GRAD_END_UNIT_OOB;
|
||||
}
|
||||
if (block->GradientStartUnit > block->GradientEndUnit)
|
||||
{
|
||||
return ERR_UNPACK_GRAD_END_UNIT_INVALID;
|
||||
}
|
||||
if (block->GradientStartValue < 0 || block->GradientStartValue >= 32)
|
||||
{
|
||||
return ERR_UNPACK_GRAD_START_VALUE_OOB;
|
||||
}
|
||||
if (block->GradientEndValue < 0 || block->GradientEndValue >= 32)
|
||||
{
|
||||
return ERR_UNPACK_GRAD_END_VALUE_OOB;
|
||||
}
|
||||
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
at9_status ReadStereoParams(block* block, bit_reader_cxt* br)
|
||||
{
|
||||
if (block->BlockType != Stereo) return ERR_SUCCESS;
|
||||
|
||||
block->PrimaryChannelIndex = read_int(br, 1);
|
||||
block->HasJointStereoSigns = read_int(br, 1);
|
||||
if (block->HasJointStereoSigns)
|
||||
{
|
||||
for (int i = block->StereoQuantizationUnit; i < block->QuantizationUnitCount; i++)
|
||||
{
|
||||
block->JointStereoSigns[i] = read_int(br, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(block->JointStereoSigns, 0, sizeof(block->JointStereoSigns));
|
||||
}
|
||||
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
void BexReadHeader(channel* channel, bit_reader_cxt* br, int bexBand)
|
||||
{
|
||||
const int bexMode = read_int(br, 2);
|
||||
channel->BexMode = bexBand > 2 ? bexMode : 4;
|
||||
channel->BexValueCount = BexEncodedValueCounts[channel->BexMode][bexBand];
|
||||
}
|
||||
|
||||
void BexReadData(channel* channel, bit_reader_cxt* br, int bexBand)
|
||||
{
|
||||
for (int i = 0; i < channel->BexValueCount; i++)
|
||||
{
|
||||
const int dataLength = BexDataLengths[channel->BexMode][bexBand][i];
|
||||
channel->BexValues[i] = read_int(br, dataLength);
|
||||
}
|
||||
}
|
||||
|
||||
at9_status ReadExtensionParams(block* block, bit_reader_cxt* br)
|
||||
{
|
||||
int bexBand = 0;
|
||||
if (block->BandExtensionEnabled)
|
||||
{
|
||||
bexBand = BexGroupInfo[block->QuantizationUnitCount - 13].band_count;
|
||||
if (block->BlockType == Stereo)
|
||||
{
|
||||
BexReadHeader(&block->Channels[1], br, bexBand);
|
||||
}
|
||||
else
|
||||
{
|
||||
br->position += 1;
|
||||
}
|
||||
}
|
||||
block->HasExtensionData = read_int(br, 1);
|
||||
|
||||
if (!block->HasExtensionData) return ERR_SUCCESS;
|
||||
if (!block->BandExtensionEnabled)
|
||||
{
|
||||
block->BexMode = read_int(br, 2);
|
||||
block->BexDataLength = read_int(br, 5);
|
||||
br->position += block->BexDataLength;
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
BexReadHeader(&block->Channels[0], br, bexBand);
|
||||
|
||||
block->BexDataLength = read_int(br, 5);
|
||||
if (block->BexDataLength <= 0) return ERR_SUCCESS;
|
||||
const int bexDataEnd = br->position + block->BexDataLength;
|
||||
|
||||
BexReadData(&block->Channels[0], br, bexBand);
|
||||
|
||||
if (block->BlockType == Stereo)
|
||||
{
|
||||
BexReadData(&block->Channels[1], br, bexBand);
|
||||
}
|
||||
|
||||
// Make sure we didn't read too many bits
|
||||
if (br->position > bexDataEnd)
|
||||
{
|
||||
return ERR_UNPACK_EXTENSION_DATA_INVALID;
|
||||
}
|
||||
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
void UpdateCodedUnits(channel* channel)
|
||||
{
|
||||
if (channel->Block->PrimaryChannelIndex == channel->ChannelIndex)
|
||||
{
|
||||
channel->CodedQuantUnits = channel->Block->QuantizationUnitCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
channel->CodedQuantUnits = channel->Block->StereoQuantizationUnit;
|
||||
}
|
||||
}
|
||||
|
||||
void CalculateSpectrumCodebookIndex(channel* channel)
|
||||
{
|
||||
memset(channel->CodebookSet, 0, sizeof(channel->CodebookSet));
|
||||
const int quantUnits = channel->CodedQuantUnits;
|
||||
int* sf = channel->ScaleFactors;
|
||||
|
||||
if (quantUnits <= 1) return;
|
||||
if (channel->config->HighSampleRate) return;
|
||||
|
||||
// Temporarily setting this value allows for simpler code by
|
||||
// making the last value a non-special case.
|
||||
const int originalScaleTmp = sf[quantUnits];
|
||||
sf[quantUnits] = sf[quantUnits - 1];
|
||||
|
||||
int avg = 0;
|
||||
if (quantUnits > 12)
|
||||
{
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
avg += sf[i];
|
||||
}
|
||||
avg = (avg + 6) / 12;
|
||||
}
|
||||
|
||||
for (int i = 8; i < quantUnits; i++)
|
||||
{
|
||||
const int prevSf = sf[i - 1];
|
||||
const int nextSf = sf[i + 1];
|
||||
const int minSf = min(prevSf, nextSf);
|
||||
if (sf[i] - minSf >= 3 || sf[i] - prevSf + sf[i] - nextSf >= 3)
|
||||
{
|
||||
channel->CodebookSet[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 12; i < quantUnits; i++)
|
||||
{
|
||||
if (channel->CodebookSet[i] == 0)
|
||||
{
|
||||
const int minSf = min(sf[i - 1], sf[i + 1]);
|
||||
if (sf[i] - minSf >= 2 && sf[i] >= avg - (QuantUnitToCoeffCount[i] == 16 ? 1 : 0))
|
||||
{
|
||||
channel->CodebookSet[i] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sf[quantUnits] = originalScaleTmp;
|
||||
}
|
||||
|
||||
at9_status ReadSpectra(channel* channel, bit_reader_cxt* br)
|
||||
{
|
||||
int values[16];
|
||||
memset(channel->QuantizedSpectra, 0, sizeof(channel->QuantizedSpectra));
|
||||
const int maxHuffPrecision = MaxHuffPrecision[channel->config->HighSampleRate];
|
||||
|
||||
for (int i = 0; i < channel->CodedQuantUnits; i++)
|
||||
{
|
||||
const int subbandCount = QuantUnitToCoeffCount[i];
|
||||
const int precision = channel->Precisions[i] + 1;
|
||||
if (precision <= maxHuffPrecision)
|
||||
{
|
||||
const HuffmanCodebook* huff = &HuffmanSpectrum[channel->CodebookSet[i]][precision][QuantUnitToCodebookIndex[i]];
|
||||
const int groupCount = subbandCount >> huff->ValueCountPower;
|
||||
for (int j = 0; j < groupCount; j++)
|
||||
{
|
||||
values[j] = ReadHuffmanValue(huff, br, FALSE);
|
||||
}
|
||||
|
||||
DecodeHuffmanValues(channel->QuantizedSpectra, QuantUnitToCoeffIndex[i], subbandCount, huff, values);
|
||||
}
|
||||
else
|
||||
{
|
||||
const int subbandIndex = QuantUnitToCoeffIndex[i];
|
||||
for (int j = subbandIndex; j < QuantUnitToCoeffIndex[i + 1]; j++)
|
||||
{
|
||||
channel->QuantizedSpectra[j] = read_signed_int(br, precision);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
at9_status ReadSpectraFine(channel* channel, bit_reader_cxt* br)
|
||||
{
|
||||
memset(channel->QuantizedSpectraFine, 0, sizeof(channel->QuantizedSpectraFine));
|
||||
|
||||
for (int i = 0; i < channel->CodedQuantUnits; i++)
|
||||
{
|
||||
if (channel->PrecisionsFine[i] > 0)
|
||||
{
|
||||
const int overflowBits = channel->PrecisionsFine[i] + 1;
|
||||
const int startSubband = QuantUnitToCoeffIndex[i];
|
||||
const int endSubband = QuantUnitToCoeffIndex[i + 1];
|
||||
|
||||
for (int j = startSubband; j < endSubband; j++)
|
||||
{
|
||||
channel->QuantizedSpectraFine[j] = read_signed_int(br, overflowBits);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
at9_status UnpackLfeBlock(block* block, bit_reader_cxt* br)
|
||||
{
|
||||
channel* channel = &block->Channels[0];
|
||||
block->QuantizationUnitCount = 2;
|
||||
|
||||
DecodeLfeScaleFactors(channel, br);
|
||||
CalculateLfePrecision(channel);
|
||||
channel->CodedQuantUnits = block->QuantizationUnitCount;
|
||||
ReadLfeSpectra(channel, br);
|
||||
|
||||
return ERR_SUCCESS;
|
||||
}
|
||||
|
||||
void DecodeLfeScaleFactors(channel* channel, bit_reader_cxt* br)
|
||||
{
|
||||
memset(channel->ScaleFactors, 0, sizeof(channel->ScaleFactors));
|
||||
for (int i = 0; i < channel->Block->QuantizationUnitCount; i++)
|
||||
{
|
||||
channel->ScaleFactors[i] = read_int(br, 5);
|
||||
}
|
||||
}
|
||||
|
||||
void CalculateLfePrecision(channel* channel)
|
||||
{
|
||||
block* block = channel->Block;
|
||||
const int precision = block->ReuseBandParams ? 8 : 4;
|
||||
for (int i = 0; i < block->QuantizationUnitCount; i++)
|
||||
{
|
||||
channel->Precisions[i] = precision;
|
||||
channel->PrecisionsFine[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ReadLfeSpectra(channel* channel, bit_reader_cxt* br)
|
||||
{
|
||||
memset(channel->QuantizedSpectra, 0, sizeof(channel->QuantizedSpectra));
|
||||
|
||||
for (int i = 0; i < channel->CodedQuantUnits; i++)
|
||||
{
|
||||
if (channel->Precisions[i] <= 0) continue;
|
||||
|
||||
const int precision = channel->Precisions[i] + 1;
|
||||
for (int j = QuantUnitToCoeffIndex[i]; j < QuantUnitToCoeffIndex[i + 1]; j++)
|
||||
{
|
||||
channel->QuantizedSpectra[j] = read_signed_int(br, precision);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
#include "bit_reader.h"
|
||||
#include "error_codes.h"
|
||||
#include "structures.h"
|
||||
|
||||
at9_status UnpackFrame(frame* frame, bit_reader_cxt* br);
|
||||
at9_status UnpackBlock(block* block, bit_reader_cxt* br);
|
||||
at9_status ReadBlockHeader(block* block, bit_reader_cxt* br);
|
||||
at9_status UnpackStandardBlock(block* block, bit_reader_cxt* br);
|
||||
at9_status ReadBandParams(block* block, bit_reader_cxt* br);
|
||||
at9_status ReadGradientParams(block* block, bit_reader_cxt* br);
|
||||
at9_status ReadStereoParams(block* block, bit_reader_cxt* br);
|
||||
at9_status ReadExtensionParams(block* block, bit_reader_cxt* br);
|
||||
void UpdateCodedUnits(channel* channel);
|
||||
void CalculateSpectrumCodebookIndex(channel* channel);
|
||||
|
||||
at9_status ReadSpectra(channel* channel, bit_reader_cxt* br);
|
||||
at9_status ReadSpectraFine(channel* channel, bit_reader_cxt* br);
|
||||
|
||||
at9_status UnpackLfeBlock(block* block, bit_reader_cxt* br);
|
||||
void DecodeLfeScaleFactors(channel* channel, bit_reader_cxt* br);
|
||||
void CalculateLfePrecision(channel* channel);
|
||||
void ReadLfeSpectra(channel* channel, bit_reader_cxt* br);
|
|
@ -0,0 +1,30 @@
|
|||
#include "utility.h"
|
||||
#include <limits.h>
|
||||
|
||||
int max(int a, int b) { return a > b ? a : b; }
|
||||
int min(int a, int b) { return a > b ? b : a; }
|
||||
|
||||
unsigned int BitReverse32(unsigned int value, int bitCount)
|
||||
{
|
||||
value = ((value & 0xaaaaaaaa) >> 1) | ((value & 0x55555555) << 1);
|
||||
value = ((value & 0xcccccccc) >> 2) | ((value & 0x33333333) << 2);
|
||||
value = ((value & 0xf0f0f0f0) >> 4) | ((value & 0x0f0f0f0f) << 4);
|
||||
value = ((value & 0xff00ff00) >> 8) | ((value & 0x00ff00ff) << 8);
|
||||
value = (value >> 16) | (value << 16);
|
||||
return value >> (32 - bitCount);
|
||||
}
|
||||
|
||||
int SignExtend32(int value, int bits)
|
||||
{
|
||||
const int shift = 8 * sizeof(int) - bits;
|
||||
return (value << shift) >> shift;
|
||||
}
|
||||
|
||||
short Clamp16(int value)
|
||||
{
|
||||
if (value > SHRT_MAX)
|
||||
return SHRT_MAX;
|
||||
if (value < SHRT_MIN)
|
||||
return SHRT_MIN;
|
||||
return (short)value;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#define FALSE 0
|
||||
#define TRUE 1
|
||||
#define NULL 0
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
int max(int a, int b);
|
||||
int min(int a, int b);
|
||||
unsigned int BitReverse32(unsigned int value, int bitCount);
|
||||
int SignExtend32(int value, int bits);
|
||||
short Clamp16(int value);
|
|
@ -0,0 +1,42 @@
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27130.2003
|
||||
MinimumVisualStudioVersion = 15.0.26124.0
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibAtrac9", "LibAtrac9\LibAtrac9.csproj", "{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|ARM = Debug|ARM
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|ARM = Release|ARM
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|x64.Build.0 = Release|Any CPU
|
||||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{2414DAA9-779C-4E7E-A97F-4DE09B22B88F}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {2AFD09F0-D995-47ED-AA86-FFA93824A514}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,119 @@
|
|||
using System.IO;
|
||||
using LibAtrac9.Utilities;
|
||||
|
||||
namespace LibAtrac9
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores the configuration data needed to decode or encode an ATRAC9 stream.
|
||||
/// </summary>
|
||||
public class Atrac9Config
|
||||
{
|
||||
/// <summary>
|
||||
/// The 4-byte ATRAC9 configuration data.
|
||||
/// </summary>
|
||||
public byte[] ConfigData { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A 4-bit value specifying one of 16 sample rates.
|
||||
/// </summary>
|
||||
public int SampleRateIndex { get; }
|
||||
/// <summary>
|
||||
/// A 3-bit value specifying one of 6 substream channel mappings.
|
||||
/// </summary>
|
||||
public int ChannelConfigIndex { get; }
|
||||
/// <summary>
|
||||
/// An 11-bit value containing the average size of a single frame.
|
||||
/// </summary>
|
||||
public int FrameBytes { get; }
|
||||
/// <summary>
|
||||
/// A 2-bit value indicating how many frames are in each superframe.
|
||||
/// </summary>
|
||||
public int SuperframeIndex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The channel mapping used by the ATRAC9 stream.
|
||||
/// </summary>
|
||||
public ChannelConfig ChannelConfig { get; }
|
||||
/// <summary>
|
||||
/// The total number of channels in the ATRAC9 stream.
|
||||
/// </summary>
|
||||
public int ChannelCount { get; }
|
||||
/// <summary>
|
||||
/// The sample rate of the ATRAC9 stream.
|
||||
/// </summary>
|
||||
public int SampleRate { get; }
|
||||
/// <summary>
|
||||
/// Indicates whether the ATRAC9 stream has a <see cref="SampleRateIndex"/> of 8 or above.
|
||||
/// </summary>
|
||||
public bool HighSampleRate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of frames in each superframe.
|
||||
/// </summary>
|
||||
public int FramesPerSuperframe { get; }
|
||||
/// <summary>
|
||||
/// The number of samples in one frame as an exponent of 2.
|
||||
/// <see cref="FrameSamples"/> = 2^<see cref="FrameSamplesPower"/>.
|
||||
/// </summary>
|
||||
public int FrameSamplesPower { get; }
|
||||
/// <summary>
|
||||
/// The number of samples in one frame.
|
||||
/// </summary>
|
||||
public int FrameSamples { get; }
|
||||
/// <summary>
|
||||
/// The number of bytes in one superframe.
|
||||
/// </summary>
|
||||
public int SuperframeBytes { get; }
|
||||
/// <summary>
|
||||
/// The number of samples in one superframe.
|
||||
/// </summary>
|
||||
public int SuperframeSamples { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Reads ATRAC9 configuration data and calculates the stream parameters from it.
|
||||
/// </summary>
|
||||
/// <param name="configData">The processed ATRAC9 configuration.</param>
|
||||
public Atrac9Config(byte[] configData)
|
||||
{
|
||||
if (configData == null || configData.Length != 4)
|
||||
{
|
||||
throw new InvalidDataException("Config data must be 4 bytes long");
|
||||
}
|
||||
|
||||
ReadConfigData(configData, out int a, out int b, out int c, out int d);
|
||||
SampleRateIndex = a;
|
||||
ChannelConfigIndex = b;
|
||||
FrameBytes = c;
|
||||
SuperframeIndex = d;
|
||||
ConfigData = configData;
|
||||
|
||||
FramesPerSuperframe = 1 << SuperframeIndex;
|
||||
SuperframeBytes = FrameBytes << SuperframeIndex;
|
||||
ChannelConfig = Tables.ChannelConfig[ChannelConfigIndex];
|
||||
|
||||
ChannelCount = ChannelConfig.ChannelCount;
|
||||
SampleRate = Tables.SampleRates[SampleRateIndex];
|
||||
HighSampleRate = SampleRateIndex > 7;
|
||||
FrameSamplesPower = Tables.SamplingRateIndexToFrameSamplesPower[SampleRateIndex];
|
||||
FrameSamples = 1 << FrameSamplesPower;
|
||||
SuperframeSamples = FrameSamples * FramesPerSuperframe;
|
||||
}
|
||||
|
||||
private static void ReadConfigData(byte[] configData, out int sampleRateIndex, out int channelConfigIndex, out int frameBytes, out int superframeIndex)
|
||||
{
|
||||
var reader = new BitReader(configData);
|
||||
|
||||
int header = reader.ReadInt(8);
|
||||
sampleRateIndex = reader.ReadInt(4);
|
||||
channelConfigIndex = reader.ReadInt(3);
|
||||
int validationBit = reader.ReadInt(1);
|
||||
frameBytes = reader.ReadInt(11) + 1;
|
||||
superframeIndex = reader.ReadInt(2);
|
||||
|
||||
if (header != 0xFE || validationBit != 0)
|
||||
{
|
||||
throw new InvalidDataException("ATRAC9 Config Data is invalid");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
using System;
|
||||
using LibAtrac9.Utilities;
|
||||
|
||||
namespace LibAtrac9
|
||||
{
|
||||
/// <summary>
|
||||
/// Decodes an ATRAC9 stream into 16-bit PCM.
|
||||
/// </summary>
|
||||
public class Atrac9Decoder
|
||||
{
|
||||
/// <summary>
|
||||
/// The config data for the current ATRAC9 stream.
|
||||
/// </summary>
|
||||
public Atrac9Config Config { get; private set; }
|
||||
|
||||
private Frame Frame { get; set; }
|
||||
private BitReader Reader { get; set; }
|
||||
private bool _initialized;
|
||||
|
||||
/// <summary>
|
||||
/// Sets up the decoder to decode an ATRAC9 stream based on the information in <paramref name="configData"/>.
|
||||
/// </summary>
|
||||
/// <param name="configData">A 4-byte value containing information about the ATRAC9 stream.</param>
|
||||
public void Initialize(byte[] configData)
|
||||
{
|
||||
Config = new Atrac9Config(configData);
|
||||
Frame = new Frame(Config);
|
||||
Reader = new BitReader(null);
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes one superframe of ATRAC9 data.
|
||||
/// </summary>
|
||||
/// <param name="atrac9Data">The ATRAC9 data to decode. The array must be at least
|
||||
/// <see cref="Config"/>.<see cref="Atrac9Config.SuperframeBytes"/> bytes long.</param>
|
||||
/// <param name="pcmOut">A buffer that the decoded PCM data will be placed in.
|
||||
/// The array must have dimensions of at least [<see cref="Config"/>.<see cref="Atrac9Config.ChannelCount"/>]
|
||||
/// [<see cref="Config"/>.<see cref="Atrac9Config.SuperframeSamples"/>].</param>
|
||||
public void Decode(byte[] atrac9Data, short[][] pcmOut)
|
||||
{
|
||||
if (!_initialized) throw new InvalidOperationException("Decoder must be initialized before decoding.");
|
||||
|
||||
ValidateDecodeBuffers(atrac9Data, pcmOut);
|
||||
Reader.SetBuffer(atrac9Data);
|
||||
DecodeSuperFrame(pcmOut);
|
||||
}
|
||||
|
||||
private void ValidateDecodeBuffers(byte[] atrac9Buffer, short[][] pcmBuffer)
|
||||
{
|
||||
if (atrac9Buffer == null) throw new ArgumentNullException(nameof(atrac9Buffer));
|
||||
if (pcmBuffer == null) throw new ArgumentNullException(nameof(pcmBuffer));
|
||||
|
||||
if (atrac9Buffer.Length < Config.SuperframeBytes)
|
||||
{
|
||||
throw new ArgumentException("ATRAC9 buffer is too small");
|
||||
}
|
||||
|
||||
if (pcmBuffer.Length < Config.ChannelCount)
|
||||
{
|
||||
throw new ArgumentException("PCM buffer is too small");
|
||||
}
|
||||
|
||||
for (int i = 0; i < Config.ChannelCount; i++)
|
||||
{
|
||||
if (pcmBuffer[i]?.Length < Config.SuperframeSamples)
|
||||
{
|
||||
throw new ArgumentException("PCM buffer is too small");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DecodeSuperFrame(short[][] pcmOut)
|
||||
{
|
||||
for (int i = 0; i < Config.FramesPerSuperframe; i++)
|
||||
{
|
||||
Frame.FrameIndex = i;
|
||||
DecodeFrame(Reader, Frame);
|
||||
PcmFloatToShort(pcmOut, i * Config.FrameSamples);
|
||||
Reader.AlignPosition(8);
|
||||
}
|
||||
}
|
||||
|
||||
private void PcmFloatToShort(short[][] pcmOut, int start)
|
||||
{
|
||||
int endSample = start + Config.FrameSamples;
|
||||
int channelNum = 0;
|
||||
foreach (Block block in Frame.Blocks)
|
||||
{
|
||||
foreach (Channel channel in block.Channels)
|
||||
{
|
||||
double[] pcmSrc = channel.Pcm;
|
||||
short[] pcmDest = pcmOut[channelNum++];
|
||||
for (int d = 0, s = start; s < endSample; d++, s++)
|
||||
{
|
||||
double sample = pcmSrc[d];
|
||||
// Not using Math.Round because it's ~20x slower on 64-bit
|
||||
int roundedSample = (int)Math.Floor(sample + 0.5);
|
||||
pcmDest[s] = Helpers.Clamp16(roundedSample);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void DecodeFrame(BitReader reader, Frame frame)
|
||||
{
|
||||
Unpack.UnpackFrame(reader, frame);
|
||||
|
||||
foreach (Block block in frame.Blocks)
|
||||
{
|
||||
Quantization.DequantizeSpectra(block);
|
||||
Stereo.ApplyIntensityStereo(block);
|
||||
Quantization.ScaleSpectrum(block);
|
||||
BandExtension.ApplyBandExtension(block);
|
||||
ImdctBlock(block);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ImdctBlock(Block block)
|
||||
{
|
||||
foreach (Channel channel in block.Channels)
|
||||
{
|
||||
channel.Mdct.RunImdct(channel.Spectra, channel.Pcm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
namespace LibAtrac9
|
||||
{
|
||||
/// <summary>
|
||||
/// An Xorshift RNG used by the ATRAC9 codec
|
||||
/// </summary>
|
||||
internal class Atrac9Rng
|
||||
{
|
||||
private ushort _stateA;
|
||||
private ushort _stateB;
|
||||
private ushort _stateC;
|
||||
private ushort _stateD;
|
||||
|
||||
public Atrac9Rng(ushort seed)
|
||||
{
|
||||
int startValue = 0x4D93 * (seed ^ (seed >> 14));
|
||||
|
||||
_stateA = (ushort)(3 - startValue);
|
||||
_stateB = (ushort)(2 - startValue);
|
||||
_stateC = (ushort)(1 - startValue);
|
||||
_stateD = (ushort)(0 - startValue);
|
||||
}
|
||||
|
||||
public ushort Next()
|
||||
{
|
||||
ushort t = (ushort)(_stateD ^ (_stateD << 5));
|
||||
_stateD = _stateC;
|
||||
_stateC = _stateB;
|
||||
_stateB = _stateA;
|
||||
_stateA = (ushort)(t ^ _stateA ^ ((t ^ (_stateA >> 5)) >> 4));
|
||||
return _stateA;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,372 @@
|
|||
using System;
|
||||
|
||||
namespace LibAtrac9
|
||||
{
|
||||
internal static class BandExtension
|
||||
{
|
||||
public static void ApplyBandExtension(Block block)
|
||||
{
|
||||
if (!block.BandExtensionEnabled || !block.HasExtensionData) return;
|
||||
|
||||
foreach (Channel channel in block.Channels)
|
||||
{
|
||||
ApplyBandExtensionChannel(channel);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ApplyBandExtensionChannel(Channel channel)
|
||||
{
|
||||
int groupAUnit = channel.Block.QuantizationUnitCount;
|
||||
int[] scaleFactors = channel.ScaleFactors;
|
||||
double[] spectra = channel.Spectra;
|
||||
double[] scales = channel.BexScales;
|
||||
int[] values = channel.BexValues;
|
||||
|
||||
GetBexBandInfo(out int bandCount, out int groupBUnit, out int groupCUnit, groupAUnit);
|
||||
int totalUnits = Math.Max(groupCUnit, 22);
|
||||
|
||||
int groupABin = Tables.QuantUnitToCoeffIndex[groupAUnit];
|
||||
int groupBBin = Tables.QuantUnitToCoeffIndex[groupBUnit];
|
||||
int groupCBin = Tables.QuantUnitToCoeffIndex[groupCUnit];
|
||||
int totalBins = Tables.QuantUnitToCoeffIndex[totalUnits];
|
||||
|
||||
FillHighFrequencies(spectra, groupABin, groupBBin, groupCBin, totalBins);
|
||||
|
||||
switch (channel.BexMode)
|
||||
{
|
||||
case 0:
|
||||
int bexQuantUnits = totalUnits - groupAUnit;
|
||||
|
||||
switch (bandCount)
|
||||
{
|
||||
case 3:
|
||||
scales[0] = BexMode0Bands3[0][values[0]];
|
||||
scales[1] = BexMode0Bands3[1][values[0]];
|
||||
scales[2] = BexMode0Bands3[2][values[1]];
|
||||
scales[3] = BexMode0Bands3[3][values[2]];
|
||||
scales[4] = BexMode0Bands3[4][values[3]];
|
||||
break;
|
||||
case 4:
|
||||
scales[0] = BexMode0Bands4[0][values[0]];
|
||||
scales[1] = BexMode0Bands4[1][values[0]];
|
||||
scales[2] = BexMode0Bands4[2][values[1]];
|
||||
scales[3] = BexMode0Bands4[3][values[2]];
|
||||
scales[4] = BexMode0Bands4[4][values[3]];
|
||||
break;
|
||||
case 5:
|
||||
scales[0] = BexMode0Bands5[0][values[0]];
|
||||
scales[1] = BexMode0Bands5[1][values[1]];
|
||||
scales[2] = BexMode0Bands5[2][values[1]];
|
||||
break;
|
||||
}
|
||||
|
||||
scales[bexQuantUnits - 1] = Tables.SpectrumScale[scaleFactors[groupAUnit]];
|
||||
|
||||
AddNoiseToSpectrum(channel, Tables.QuantUnitToCoeffIndex[totalUnits - 1],
|
||||
Tables.QuantUnitToCoeffCount[totalUnits - 1]);
|
||||
ScaleBexQuantUnits(spectra, scales, groupAUnit, totalUnits);
|
||||
break;
|
||||
case 1:
|
||||
for (int i = groupAUnit; i < totalUnits; i++)
|
||||
{
|
||||
scales[i - groupAUnit] = Tables.SpectrumScale[scaleFactors[i]];
|
||||
}
|
||||
|
||||
AddNoiseToSpectrum(channel, groupABin, totalBins - groupABin);
|
||||
ScaleBexQuantUnits(spectra, scales, groupAUnit, totalUnits);
|
||||
break;
|
||||
case 2:
|
||||
double groupAScale2 = BexMode2Scale[values[0]];
|
||||
double groupBScale2 = BexMode2Scale[values[1]];
|
||||
|
||||
for (int i = groupABin; i < groupBBin; i++)
|
||||
{
|
||||
spectra[i] *= groupAScale2;
|
||||
}
|
||||
|
||||
for (int i = groupBBin; i < groupCBin; i++)
|
||||
{
|
||||
spectra[i] *= groupBScale2;
|
||||
}
|
||||
return;
|
||||
case 3:
|
||||
double rate = Math.Pow(2, BexMode3Rate[values[1]]);
|
||||
double scale = BexMode3Initial[values[0]];
|
||||
for (int i = groupABin; i < totalBins; i++)
|
||||
{
|
||||
scale *= rate;
|
||||
spectra[i] *= scale;
|
||||
}
|
||||
return;
|
||||
case 4:
|
||||
double mult = BexMode4Multiplier[values[0]];
|
||||
double groupAScale4 = 0.7079468 * mult;
|
||||
double groupBScale4 = 0.5011902 * mult;
|
||||
double groupCScale4 = 0.3548279 * mult;
|
||||
|
||||
for (int i = groupABin; i < groupBBin; i++)
|
||||
{
|
||||
spectra[i] *= groupAScale4;
|
||||
}
|
||||
|
||||
for (int i = groupBBin; i < groupCBin; i++)
|
||||
{
|
||||
spectra[i] *= groupBScale4;
|
||||
}
|
||||
|
||||
for (int i = groupCBin; i < totalBins; i++)
|
||||
{
|
||||
spectra[i] *= groupCScale4;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ScaleBexQuantUnits(double[] spectra, double[] scales, int startUnit, int totalUnits)
|
||||
{
|
||||
for (int i = startUnit; i < totalUnits; i++)
|
||||
{
|
||||
for (int k = Tables.QuantUnitToCoeffIndex[i]; k < Tables.QuantUnitToCoeffIndex[i + 1]; k++)
|
||||
{
|
||||
spectra[k] *= scales[i - startUnit];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void FillHighFrequencies(double[] spectra, int groupABin, int groupBBin, int groupCBin, int totalBins)
|
||||
{
|
||||
for (int i = 0; i < groupBBin - groupABin; i++)
|
||||
{
|
||||
spectra[groupABin + i] = spectra[groupABin - i - 1];
|
||||
}
|
||||
|
||||
for (int i = 0; i < groupCBin - groupBBin; i++)
|
||||
{
|
||||
spectra[groupBBin + i] = spectra[groupBBin - i - 1];
|
||||
}
|
||||
|
||||
for (int i = 0; i < totalBins - groupCBin; i++)
|
||||
{
|
||||
spectra[groupCBin + i] = spectra[groupCBin - i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddNoiseToSpectrum(Channel channel, int index, int count)
|
||||
{
|
||||
if (channel.Rng == null)
|
||||
{
|
||||
int[] sf = channel.ScaleFactors;
|
||||
ushort seed = (ushort)(543 * (sf[8] + sf[12] + sf[15] + 1));
|
||||
channel.Rng = new Atrac9Rng(seed);
|
||||
}
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
channel.Spectra[i + index] = channel.Rng.Next() / 65535.0 * 2.0 - 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
public static void GetBexBandInfo(out int bandCount, out int groupAUnit, out int groupBUnit, int quantUnits)
|
||||
{
|
||||
groupAUnit = BexGroupInfo[quantUnits - 13][0];
|
||||
groupBUnit = BexGroupInfo[quantUnits - 13][1];
|
||||
bandCount = BexGroupInfo[quantUnits - 13][2];
|
||||
}
|
||||
|
||||
public static readonly byte[][] BexGroupInfo =
|
||||
{
|
||||
new byte[] {16, 21, 0},
|
||||
new byte[] {18, 22, 1},
|
||||
new byte[] {20, 22, 2},
|
||||
new byte[] {21, 22, 3},
|
||||
new byte[] {21, 22, 3},
|
||||
new byte[] {23, 24, 4},
|
||||
new byte[] {23, 24, 4},
|
||||
new byte[] {24, 24, 5}
|
||||
};
|
||||
|
||||
// [mode][bands]
|
||||
|
||||
public static readonly byte[][] BexEncodedValueCounts =
|
||||
{
|
||||
new byte[] {0, 0, 0, 4, 4, 2},
|
||||
new byte[] {0, 0, 0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 2, 2, 1},
|
||||
new byte[] {0, 0, 0, 2, 2, 2},
|
||||
new byte[] {1, 1, 1, 0, 0, 0}
|
||||
};
|
||||
|
||||
// [mode][bands][valueIndex]
|
||||
|
||||
public static readonly byte[][][] BexDataLengths =
|
||||
{
|
||||
new[] {
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {5, 4, 3, 3},
|
||||
new byte[] {4, 4, 3, 4},
|
||||
new byte[] {4, 5, 0, 0}
|
||||
}, new[] {
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0}
|
||||
}, new[] {
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {6, 6, 0, 0},
|
||||
new byte[] {6, 6, 0, 0},
|
||||
new byte[] {6, 0, 0, 0}
|
||||
}, new[] {
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {4, 4, 0, 0},
|
||||
new byte[] {4, 4, 0, 0},
|
||||
new byte[] {4, 4, 0, 0}
|
||||
}, new[] {
|
||||
new byte[] {3, 0, 0, 0},
|
||||
new byte[] {3, 0, 0, 0},
|
||||
new byte[] {3, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0},
|
||||
new byte[] {0, 0, 0, 0}
|
||||
}
|
||||
};
|
||||
|
||||
public static readonly double[][] BexMode0Bands3 =
|
||||
{
|
||||
new[] {
|
||||
0.000000e+0, 1.988220e-1, 2.514343e-1, 2.960510e-1,
|
||||
3.263550e-1, 3.771362e-1, 3.786926e-1, 4.540405e-1,
|
||||
4.877625e-1, 5.262451e-1, 5.447083e-1, 5.737000e-1,
|
||||
6.212158e-1, 6.222839e-1, 6.560974e-1, 6.896667e-1,
|
||||
7.555542e-1, 7.677917e-1, 7.918091e-1, 7.971497e-1,
|
||||
8.188171e-1, 8.446045e-1, 9.790649e-1, 9.822083e-1,
|
||||
9.846191e-1, 9.859314e-1, 9.863586e-1, 9.863892e-1,
|
||||
9.873352e-1, 9.881287e-1, 9.898682e-1, 9.913330e-1
|
||||
}, new[] {
|
||||
0.000000e+0, 9.982910e-1, 7.592773e-2, 7.179565e-1,
|
||||
9.851379e-1, 5.340271e-1, 9.013672e-1, 6.349182e-1,
|
||||
7.226257e-1, 1.948547e-1, 7.628174e-1, 9.873657e-1,
|
||||
8.112183e-1, 2.715454e-1, 9.734192e-1, 1.443787e-1,
|
||||
4.640198e-1, 3.249207e-1, 3.790894e-1, 8.276367e-2,
|
||||
5.954590e-1, 2.864380e-1, 9.806824e-1, 7.929077e-1,
|
||||
6.292114e-1, 4.887085e-1, 2.905273e-1, 1.301880e-1,
|
||||
3.140869e-1, 5.482483e-1, 4.210815e-1, 1.182861e-1
|
||||
}, new[] {
|
||||
0.000000e+0, 3.155518e-2, 8.581543e-2, 1.364746e-1,
|
||||
1.858826e-1, 2.368469e-1, 2.888184e-1, 3.432617e-1,
|
||||
4.012451e-1, 4.623108e-1, 5.271301e-1, 5.954895e-1,
|
||||
6.681213e-1, 7.448425e-1, 8.245239e-1, 9.097290e-1
|
||||
}, new[] {
|
||||
0.000000e+0, 4.418945e-2, 1.303711e-1, 2.273560e-1,
|
||||
3.395996e-1, 4.735718e-1, 6.267090e-1, 8.003845e-1
|
||||
}, new[] {
|
||||
0.000000e+0, 2.804565e-2, 9.683228e-2, 1.849976e-1,
|
||||
3.005981e-1, 4.470520e-1, 6.168518e-1, 8.007813e-1
|
||||
}
|
||||
};
|
||||
|
||||
public static readonly double[][] BexMode0Bands4 =
|
||||
{
|
||||
new[] {
|
||||
0.000000e+0, 2.708740e-1, 3.479614e-1, 3.578186e-1,
|
||||
5.083618e-1, 5.299072e-1, 5.819092e-1, 6.381836e-1,
|
||||
7.276917e-1, 7.595520e-1, 7.878723e-1, 9.707336e-1,
|
||||
9.713135e-1, 9.736023e-1, 9.759827e-1, 9.832458e-1
|
||||
}, new[] {
|
||||
0.000000e+0, 2.330627e-1, 5.891418e-1, 7.170410e-1,
|
||||
2.036438e-1, 1.613464e-1, 6.668701e-1, 9.481201e-1,
|
||||
9.769897e-1, 5.111694e-1, 3.522644e-1, 8.209534e-1,
|
||||
2.933960e-1, 9.757690e-1, 5.289917e-1, 4.372253e-1
|
||||
}, new[] {
|
||||
0.000000e+0, 4.360962e-2, 1.056519e-1, 1.590576e-1,
|
||||
2.078857e-1, 2.572937e-1, 3.082581e-1, 3.616028e-1,
|
||||
4.191589e-1, 4.792175e-1, 5.438538e-1, 6.125183e-1,
|
||||
6.841125e-1, 7.589417e-1, 8.365173e-1, 9.148254e-1
|
||||
}, new[] {
|
||||
0.000000e+0, 4.074097e-2, 1.164551e-1, 2.077026e-1,
|
||||
3.184509e-1, 4.532166e-1, 6.124268e-1, 7.932129e-1
|
||||
}, new[] {
|
||||
0.000000e+0, 8.880615e-3, 2.932739e-2, 5.593872e-2,
|
||||
8.825684e-2, 1.259155e-1, 1.721497e-1, 2.270813e-1,
|
||||
2.901611e-1, 3.579712e-1, 4.334106e-1, 5.147095e-1,
|
||||
6.023254e-1, 6.956177e-1, 7.952881e-1, 8.977356e-1
|
||||
}
|
||||
};
|
||||
|
||||
public static readonly double[][] BexMode0Bands5 =
|
||||
{
|
||||
new[] {
|
||||
0.000000e+0, 7.379150e-2, 1.806335e-1, 2.687073e-1,
|
||||
3.407898e-1, 4.047546e-1, 4.621887e-1, 5.168762e-1,
|
||||
5.703125e-1, 6.237488e-1, 6.763611e-1, 7.288208e-1,
|
||||
7.808533e-1, 8.337708e-1, 8.874512e-1, 9.418030e-1
|
||||
}, new[] {
|
||||
0.000000e+0, 7.980347e-2, 1.615295e-1, 1.665649e-1,
|
||||
1.822205e-1, 2.185669e-1, 2.292175e-1, 2.456665e-1,
|
||||
2.666321e-1, 3.306580e-1, 3.330688e-1, 3.765259e-1,
|
||||
4.085083e-1, 4.400024e-1, 4.407654e-1, 4.817505e-1,
|
||||
4.924011e-1, 5.320740e-1, 5.893860e-1, 6.131287e-1,
|
||||
6.212463e-1, 6.278076e-1, 6.308899e-1, 7.660828e-1,
|
||||
7.850647e-1, 7.910461e-1, 7.929382e-1, 8.038330e-1,
|
||||
9.834900e-1, 9.846191e-1, 9.852295e-1, 9.862671e-1
|
||||
}, new[] {
|
||||
0.000000e+0, 6.084290e-1, 3.672791e-1, 3.151855e-1,
|
||||
1.488953e-1, 2.571716e-1, 5.103455e-1, 3.311157e-1,
|
||||
5.426025e-2, 4.254456e-1, 7.998352e-1, 7.873230e-1,
|
||||
5.418701e-1, 2.925110e-1, 8.468628e-2, 1.410522e-1,
|
||||
9.819641e-1, 9.609070e-1, 3.530884e-2, 9.729004e-2,
|
||||
5.758362e-1, 9.941711e-1, 7.215576e-1, 7.183228e-1,
|
||||
2.028809e-1, 9.588623e-2, 2.032166e-1, 1.338806e-1,
|
||||
5.003357e-1, 1.874390e-1, 9.804993e-1, 1.107788e-1
|
||||
}
|
||||
};
|
||||
|
||||
public static readonly double[] BexMode2Scale =
|
||||
{
|
||||
4.272461e-4, 1.312256e-3, 2.441406e-3, 3.692627e-3,
|
||||
4.913330e-3, 6.134033e-3, 7.507324e-3, 8.972168e-3,
|
||||
1.049805e-2, 1.223755e-2, 1.406860e-2, 1.599121e-2,
|
||||
1.800537e-2, 2.026367e-2, 2.264404e-2, 2.517700e-2,
|
||||
2.792358e-2, 3.073120e-2, 3.344727e-2, 3.631592e-2,
|
||||
3.952026e-2, 4.275513e-2, 4.608154e-2, 4.968262e-2,
|
||||
5.355835e-2, 5.783081e-2, 6.195068e-2, 6.677246e-2,
|
||||
7.196045e-2, 7.745361e-2, 8.319092e-2, 8.993530e-2,
|
||||
9.759521e-2, 1.056213e-1, 1.138916e-1, 1.236267e-1,
|
||||
1.348267e-1, 1.470337e-1, 1.603394e-1, 1.755676e-1,
|
||||
1.905823e-1, 2.071228e-1, 2.245178e-1, 2.444153e-1,
|
||||
2.658997e-1, 2.897644e-1, 3.146057e-1, 3.450012e-1,
|
||||
3.766174e-1, 4.122620e-1, 4.505615e-1, 4.893799e-1,
|
||||
5.305481e-1, 5.731201e-1, 6.157837e-1, 6.580811e-1,
|
||||
6.985168e-1, 7.435303e-1, 7.865906e-1, 8.302612e-1,
|
||||
8.718567e-1, 9.125671e-1, 9.575806e-1, 9.996643e-1
|
||||
};
|
||||
|
||||
public static readonly double[] BexMode3Initial =
|
||||
{
|
||||
3.491211e-1, 5.371094e-1, 6.782227e-1, 7.910156e-1,
|
||||
9.057617e-1, 1.024902e+0, 1.156250e+0, 1.290527e+0,
|
||||
1.458984e+0, 1.664551e+0, 1.929688e+0, 2.278320e+0,
|
||||
2.831543e+0, 3.659180e+0, 5.257813e+0, 8.373047e+0
|
||||
};
|
||||
|
||||
public static readonly double[] BexMode3Rate =
|
||||
{
|
||||
-2.913818e-1, -2.541504e-1, -1.664429e-1, -1.476440e-1,
|
||||
-1.342163e-1, -1.220703e-1, -1.117554e-1, -1.026611e-1,
|
||||
-9.436035e-2, -8.483887e-2, -7.476807e-2, -6.304932e-2,
|
||||
-4.492188e-2, -2.447510e-2, +1.831055e-4, +4.174805e-2
|
||||
};
|
||||
|
||||
public static readonly double[] BexMode4Multiplier =
|
||||
{
|
||||
3.610229e-2, 1.260681e-1, 2.227478e-1, 3.338318e-1,
|
||||
4.662170e-1, 6.221313e-1, 7.989197e-1, 9.939575e-1
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
using System;
|
||||
|
||||
namespace LibAtrac9
|
||||
{
|
||||
internal static class BitAllocation
|
||||
{
|
||||
public static void CreateGradient(Block block)
|
||||
{
|
||||
int valueCount = block.GradientEndValue - block.GradientStartValue;
|
||||
int unitCount = block.GradientEndUnit - block.GradientStartUnit;
|
||||
|
||||
for (int i = 0; i < block.GradientEndUnit; i++)
|
||||
{
|
||||
block.Gradient[i] = block.GradientStartValue;
|
||||
}
|
||||
|
||||
for (int i = block.GradientEndUnit; i <= block.QuantizationUnitCount; i++)
|
||||
{
|
||||
block.Gradient[i] = block.GradientEndValue;
|
||||
}
|
||||
if (unitCount <= 0) return;
|
||||
if (valueCount == 0) return;
|
||||
|
||||
byte[] curve = Tables.GradientCurves[unitCount - 1];
|
||||
if (valueCount <= 0)
|
||||
{
|
||||
double scale = (-valueCount - 1) / 31.0;
|
||||
int baseVal = block.GradientStartValue - 1;
|
||||
for (int i = block.GradientStartUnit; i < block.GradientEndUnit; i++)
|
||||
{
|
||||
block.Gradient[i] = baseVal - (int)(curve[i - block.GradientStartUnit] * scale);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
double scale = (valueCount - 1) / 31.0;
|
||||
int baseVal = block.GradientStartValue + 1;
|
||||
for (int i = block.GradientStartUnit; i < block.GradientEndUnit; i++)
|
||||
{
|
||||
block.Gradient[i] = baseVal + (int)(curve[i - block.GradientStartUnit] * scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void CalculateMask(Channel channel)
|
||||
{
|
||||
Array.Clear(channel.PrecisionMask, 0, channel.PrecisionMask.Length);
|
||||
for (int i = 1; i < channel.Block.QuantizationUnitCount; i++)
|
||||
{
|
||||
int delta = channel.ScaleFactors[i] - channel.ScaleFactors[i - 1];
|
||||
if (delta > 1)
|
||||
{
|
||||
channel.PrecisionMask[i] += Math.Min(delta - 1, 5);
|
||||
}
|
||||
else if (delta < -1)
|
||||
{
|
||||
channel.PrecisionMask[i - 1] += Math.Min(delta * -1 - 1, 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void CalculatePrecisions(Channel channel)
|
||||
{
|
||||
Block block = channel.Block;
|
||||
|
||||
if (block.GradientMode != 0)
|
||||
{
|
||||
for (int i = 0; i < block.QuantizationUnitCount; i++)
|
||||
{
|
||||
channel.Precisions[i] = channel.ScaleFactors[i] + channel.PrecisionMask[i] - block.Gradient[i];
|
||||
if (channel.Precisions[i] > 0)
|
||||
{
|
||||
switch (block.GradientMode)
|
||||
{
|
||||
case 1:
|
||||
channel.Precisions[i] /= 2;
|
||||
break;
|
||||
case 2:
|
||||
channel.Precisions[i] = 3 * channel.Precisions[i] / 8;
|
||||
break;
|
||||
case 3:
|
||||
channel.Precisions[i] /= 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < block.QuantizationUnitCount; i++)
|
||||
{
|
||||
channel.Precisions[i] = channel.ScaleFactors[i] - block.Gradient[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < block.QuantizationUnitCount; i++)
|
||||
{
|
||||
if (channel.Precisions[i] < 1)
|
||||
{
|
||||
channel.Precisions[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < block.GradientBoundary; i++)
|
||||
{
|
||||
channel.Precisions[i]++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < block.QuantizationUnitCount; i++)
|
||||
{
|
||||
channel.PrecisionsFine[i] = 0;
|
||||
if (channel.Precisions[i] > 15)
|
||||
{
|
||||
channel.PrecisionsFine[i] = channel.Precisions[i] - 15;
|
||||
channel.Precisions[i] = 15;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[][] GenerateGradientCurves()
|
||||
{
|
||||
byte[] main =
|
||||
{
|
||||
01, 01, 01, 01, 02, 02, 02, 02, 03, 03, 03, 04, 04, 05, 05, 06, 07, 08, 09, 10, 11, 12, 13, 15,
|
||||
16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27, 27, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30
|
||||
};
|
||||
var curves = new byte[main.Length][];
|
||||
|
||||
for (int length = 1; length <= main.Length; length++)
|
||||
{
|
||||
curves[length - 1] = new byte[length];
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
curves[length - 1][i] = main[i * main.Length / length];
|
||||
}
|
||||
}
|
||||
return curves;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
namespace LibAtrac9
|
||||
{
|
||||
internal class Block
|
||||
{
|
||||
public Atrac9Config Config { get; }
|
||||
public BlockType BlockType { get; }
|
||||
public int BlockIndex { get; }
|
||||
public Frame Frame { get; }
|
||||
|
||||
public Channel[] Channels { get; }
|
||||
public int ChannelCount { get; }
|
||||
|
||||
public bool FirstInSuperframe { get; set; }
|
||||
public bool ReuseBandParams { get; set; }
|
||||
|
||||
public int BandCount { get; set; }
|
||||
public int StereoBand { get; set; }
|
||||
public int ExtensionBand { get; set; }
|
||||
public int QuantizationUnitCount { get; set; }
|
||||
public int StereoQuantizationUnit { get; set; }
|
||||
public int ExtensionUnit { get; set; }
|
||||
public int QuantizationUnitsPrev { get; set; }
|
||||
|
||||
public int[] Gradient { get; } = new int[31];
|
||||
public int GradientMode { get; set; }
|
||||
public int GradientStartUnit { get; set; }
|
||||
public int GradientStartValue { get; set; }
|
||||
public int GradientEndUnit { get; set; }
|
||||
public int GradientEndValue { get; set; }
|
||||
public int GradientBoundary { get; set; }
|
||||
|
||||
public int PrimaryChannelIndex { get; set; }
|
||||
public int[] JointStereoSigns { get; } = new int[30];
|
||||
public bool HasJointStereoSigns { get; set; }
|
||||
public Channel PrimaryChannel => Channels[PrimaryChannelIndex == 0 ? 0 : 1];
|
||||
public Channel SecondaryChannel => Channels[PrimaryChannelIndex == 0 ? 1 : 0];
|
||||
|
||||
public bool BandExtensionEnabled { get; set; }
|
||||
public bool HasExtensionData { get; set; }
|
||||
public int BexDataLength { get; set; }
|
||||
public int BexMode { get; set; }
|
||||
|
||||
public Block(Frame parentFrame, int blockIndex)
|
||||
{
|
||||
Frame = parentFrame;
|
||||
BlockIndex = blockIndex;
|
||||
Config = parentFrame.Config;
|
||||
BlockType = Config.ChannelConfig.BlockTypes[blockIndex];
|
||||
ChannelCount = BlockTypeToChannelCount(BlockType);
|
||||
Channels = new Channel[ChannelCount];
|
||||
for (int i = 0; i < ChannelCount; i++)
|
||||
{
|
||||
Channels[i] = new Channel(this, i);
|
||||
}
|
||||
}
|
||||
|
||||
public static int BlockTypeToChannelCount(BlockType blockType)
|
||||
{
|
||||
switch (blockType)
|
||||
{
|
||||
case BlockType.Mono:
|
||||
return 1;
|
||||
case BlockType.Stereo:
|
||||
return 2;
|
||||
case BlockType.LFE:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An ATRAC9 block (substream) type
|
||||
/// </summary>
|
||||
public enum BlockType
|
||||
{
|
||||
/// <summary>
|
||||
/// Mono ATRAC9 block
|
||||
/// </summary>
|
||||
Mono = 0,
|
||||
/// <summary>
|
||||
/// Stereo ATRAC9 block
|
||||
/// </summary>
|
||||
Stereo = 1,
|
||||
/// <summary>
|
||||
/// Low-frequency effects ATRAC9 block
|
||||
/// </summary>
|
||||
LFE = 2
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
using LibAtrac9.Utilities;
|
||||
|
||||
namespace LibAtrac9
|
||||
{
|
||||
internal class Channel
|
||||
{
|
||||
public Atrac9Config Config { get; }
|
||||
public int ChannelIndex { get; }
|
||||
public bool IsPrimary => Block.PrimaryChannelIndex == ChannelIndex;
|
||||
public Block Block { get; }
|
||||
public Mdct Mdct { get; }
|
||||
|
||||
public double[] Pcm { get; } = new double[256];
|
||||
public double[] Spectra { get; } = new double[256];
|
||||
|
||||
public int CodedQuantUnits { get; set; }
|
||||
public int ScaleFactorCodingMode { get; set; }
|
||||
public int[] ScaleFactors { get; } = new int[31];
|
||||
public int[] ScaleFactorsPrev { get; } = new int[31];
|
||||
|
||||
public int[] Precisions { get; } = new int[30];
|
||||
public int[] PrecisionsFine { get; } = new int[30];
|
||||
public int[] PrecisionMask { get; } = new int[30];
|
||||
|
||||
public int[] SpectraValuesBuffer { get; } = new int[16];
|
||||
public int[] CodebookSet { get; } = new int[30];
|
||||
|
||||
public int[] QuantizedSpectra { get; } = new int[256];
|
||||
public int[] QuantizedSpectraFine { get; } = new int[256];
|
||||
|
||||
public int BexMode { get; set; }
|
||||
public int BexValueCount { get; set; }
|
||||
public int[] BexValues { get; } = new int[4];
|
||||
public double[] BexScales { get; } = new double[6];
|
||||
public Atrac9Rng Rng { get; set; }
|
||||
|
||||
public Channel(Block parentBlock, int channelIndex)
|
||||
{
|
||||
Block = parentBlock;
|
||||
ChannelIndex = channelIndex;
|
||||
Config = parentBlock.Config;
|
||||
Mdct = new Mdct(Config.FrameSamplesPower, Tables.ImdctWindow[Config.FrameSamplesPower - 6]);
|
||||
}
|
||||
|
||||
public void UpdateCodedUnits() =>
|
||||
CodedQuantUnits = IsPrimary ? Block.QuantizationUnitCount : Block.StereoQuantizationUnit;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
namespace LibAtrac9
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes the channel mapping for an ATRAC9 stream
|
||||
/// </summary>
|
||||
public class ChannelConfig
|
||||
{
|
||||
internal ChannelConfig(params BlockType[] blockTypes)
|
||||
{
|
||||
BlockCount = blockTypes.Length;
|
||||
BlockTypes = blockTypes;
|
||||
foreach (BlockType type in blockTypes)
|
||||
{
|
||||
ChannelCount += Block.BlockTypeToChannelCount(type);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The number of blocks or substreams in the ATRAC9 stream
|
||||
/// </summary>
|
||||
public int BlockCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of each block or substream in the ATRAC9 stream
|
||||
/// </summary>
|
||||
public BlockType[] BlockTypes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of channels in the ATRAC9 stream
|
||||
/// </summary>
|
||||
public int ChannelCount { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
namespace LibAtrac9
|
||||
{
|
||||
internal class Frame
|
||||
{
|
||||
public Atrac9Config Config { get; }
|
||||
public int FrameIndex { get; set; }
|
||||
public Block[] Blocks { get; }
|
||||
|
||||
public Frame(Atrac9Config config)
|
||||
{
|
||||
Config = config;
|
||||
Blocks = new Block[config.ChannelConfig.BlockCount];
|
||||
|
||||
for (int i = 0; i < config.ChannelConfig.BlockCount; i++)
|
||||
{
|
||||
Blocks[i] = new Block(this, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
using System;
|
||||
using LibAtrac9.Utilities;
|
||||
|
||||
namespace LibAtrac9
|
||||
{
|
||||
internal class HuffmanCodebook
|
||||
{
|
||||
public HuffmanCodebook(short[] codes, byte[] bits, byte valueCountPower)
|
||||
{
|
||||
Codes = codes;
|
||||
Bits = bits;
|
||||
if (Codes == null || Bits == null) return;
|
||||
|
||||
ValueCount = 1 << valueCountPower;
|
||||
ValueCountPower = valueCountPower;
|
||||
ValueBits = Helpers.Log2(codes.Length) >> valueCountPower;
|
||||
ValueMax = 1 << ValueBits;
|
||||
|
||||
int max = 0;
|
||||
foreach (byte bitSize in bits)
|
||||
{
|
||||
max = Math.Max(max, bitSize);
|
||||
}
|
||||
|
||||
MaxBitSize = max;
|
||||
Lookup = CreateLookupTable();
|
||||
}
|
||||
|
||||
private byte[] CreateLookupTable()
|
||||
{
|
||||
if (Codes == null || Bits == null) return null;
|
||||
|
||||
int tableSize = 1 << MaxBitSize;
|
||||
var dest = new byte[tableSize];
|
||||
|
||||
for (int i = 0; i < Bits.Length; i++)
|
||||
{
|
||||
if (Bits[i] == 0) continue;
|
||||
int unusedBits = MaxBitSize - Bits[i];
|
||||
|
||||
int start = Codes[i] << unusedBits;
|
||||
int length = 1 << unusedBits;
|
||||
int end = start + length;
|
||||
|
||||
for (int j = start; j < end; j++)
|
||||
{
|
||||
dest[j] = (byte)i;
|
||||
}
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
public short[] Codes { get; }
|
||||
public byte[] Bits { get; }
|
||||
public byte[] Lookup { get; }
|
||||
public int ValueCount { get; }
|
||||
public int ValueCountPower { get; }
|
||||
public int ValueBits { get; }
|
||||
public int ValueMax { get; }
|
||||
public int MaxBitSize { get; }
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,8 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard1.0</TargetFramework>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
|
||||
namespace LibAtrac9
|
||||
{
|
||||
internal static class Quantization
|
||||
{
|
||||
public static void DequantizeSpectra(Block block)
|
||||
{
|
||||
foreach (Channel channel in block.Channels)
|
||||
{
|
||||
Array.Clear(channel.Spectra, 0, channel.Spectra.Length);
|
||||
|
||||
for (int i = 0; i < channel.CodedQuantUnits; i++)
|
||||
{
|
||||
DequantizeQuantUnit(channel, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void DequantizeQuantUnit(Channel channel, int band)
|
||||
{
|
||||
int subBandIndex = Tables.QuantUnitToCoeffIndex[band];
|
||||
int subBandCount = Tables.QuantUnitToCoeffCount[band];
|
||||
double stepSize = Tables.QuantizerStepSize[channel.Precisions[band]];
|
||||
double stepSizeFine = Tables.QuantizerFineStepSize[channel.PrecisionsFine[band]];
|
||||
|
||||
for (int sb = 0; sb < subBandCount; sb++)
|
||||
{
|
||||
double coarse = channel.QuantizedSpectra[subBandIndex + sb] * stepSize;
|
||||
double fine = channel.QuantizedSpectraFine[subBandIndex + sb] * stepSizeFine;
|
||||
channel.Spectra[subBandIndex + sb] = coarse + fine;
|
||||
}
|
||||
}
|
||||
|
||||
public static void ScaleSpectrum(Block block)
|
||||
{
|
||||
foreach (Channel channel in block.Channels)
|
||||
{
|
||||
ScaleSpectrum(channel);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ScaleSpectrum(Channel channel)
|
||||
{
|
||||
int quantUnitCount = channel.Block.QuantizationUnitCount;
|
||||
double[] spectra = channel.Spectra;
|
||||
|
||||
for (int i = 0; i < quantUnitCount; i++)
|
||||
{
|
||||
for (int sb = Tables.QuantUnitToCoeffIndex[i]; sb < Tables.QuantUnitToCoeffIndex[i + 1]; sb++)
|
||||
{
|
||||
spectra[sb] *= Tables.SpectrumScale[channel.ScaleFactors[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using LibAtrac9.Utilities;
|
||||
|
||||
namespace LibAtrac9
|
||||
{
|
||||
internal static class ScaleFactors
|
||||
{
|
||||
public static void Read(BitReader reader, Channel channel)
|
||||
{
|
||||
Array.Clear(channel.ScaleFactors, 0, channel.ScaleFactors.Length);
|
||||
|
||||
channel.ScaleFactorCodingMode = reader.ReadInt(2);
|
||||
if (channel.ChannelIndex == 0)
|
||||
{
|
||||
switch (channel.ScaleFactorCodingMode)
|
||||
{
|
||||
case 0:
|
||||
ReadVlcDeltaOffset(reader, channel);
|
||||
break;
|
||||
case 1:
|
||||
ReadClcOffset(reader, channel);
|
||||
break;
|
||||
case 2:
|
||||
if (channel.Block.FirstInSuperframe) throw new InvalidDataException();
|
||||
ReadVlcDistanceToBaseline(reader, channel, channel.ScaleFactorsPrev, channel.Block.QuantizationUnitsPrev);
|
||||
break;
|
||||
case 3:
|
||||
if (channel.Block.FirstInSuperframe) throw new InvalidDataException();
|
||||
ReadVlcDeltaOffsetWithBaseline(reader, channel, channel.ScaleFactorsPrev, channel.Block.QuantizationUnitsPrev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (channel.ScaleFactorCodingMode)
|
||||
{
|
||||
case 0:
|
||||
ReadVlcDeltaOffset(reader, channel);
|
||||
break;
|
||||
case 1:
|
||||
ReadVlcDistanceToBaseline(reader, channel, channel.Block.Channels[0].ScaleFactors, channel.Block.ExtensionUnit);
|
||||
break;
|
||||
case 2:
|
||||
ReadVlcDeltaOffsetWithBaseline(reader, channel, channel.Block.Channels[0].ScaleFactors, channel.Block.ExtensionUnit);
|
||||
break;
|
||||
case 3:
|
||||
if (channel.Block.FirstInSuperframe) throw new InvalidDataException();
|
||||
ReadVlcDistanceToBaseline(reader, channel, channel.ScaleFactorsPrev, channel.Block.QuantizationUnitsPrev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < channel.Block.ExtensionUnit; i++)
|
||||
{
|
||||
if (channel.ScaleFactors[i] < 0 || channel.ScaleFactors[i] > 31)
|
||||
{
|
||||
throw new InvalidDataException("Scale factor values are out of range.");
|
||||
}
|
||||
}
|
||||
|
||||
Array.Copy(channel.ScaleFactors, channel.ScaleFactorsPrev, channel.ScaleFactors.Length);
|
||||
}
|
||||
|
||||
private static void ReadClcOffset(BitReader reader, Channel channel)
|
||||
{
|
||||
const int maxBits = 5;
|
||||
int[] sf = channel.ScaleFactors;
|
||||
int bitLength = reader.ReadInt(2) + 2;
|
||||
int baseValue = bitLength < maxBits ? reader.ReadInt(maxBits) : 0;
|
||||
|
||||
for (int i = 0; i < channel.Block.ExtensionUnit; i++)
|
||||
{
|
||||
sf[i] = reader.ReadInt(bitLength) + baseValue;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReadVlcDeltaOffset(BitReader reader, Channel channel)
|
||||
{
|
||||
int weightIndex = reader.ReadInt(3);
|
||||
byte[] weights = ScaleFactorWeights[weightIndex];
|
||||
|
||||
int[] sf = channel.ScaleFactors;
|
||||
int baseValue = reader.ReadInt(5);
|
||||
int bitLength = reader.ReadInt(2) + 3;
|
||||
HuffmanCodebook codebook = Tables.HuffmanScaleFactorsUnsigned[bitLength];
|
||||
|
||||
sf[0] = reader.ReadInt(bitLength);
|
||||
|
||||
for (int i = 1; i < channel.Block.ExtensionUnit; i++)
|
||||
{
|
||||
int delta = Unpack.ReadHuffmanValue(codebook, reader);
|
||||
sf[i] = (sf[i - 1] + delta) & (codebook.ValueMax - 1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < channel.Block.ExtensionUnit; i++)
|
||||
{
|
||||
sf[i] += baseValue - weights[i];
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReadVlcDistanceToBaseline(BitReader reader, Channel channel, int[] baseline, int baselineLength)
|
||||
{
|
||||
int[] sf = channel.ScaleFactors;
|
||||
int bitLength = reader.ReadInt(2) + 2;
|
||||
HuffmanCodebook codebook = Tables.HuffmanScaleFactorsSigned[bitLength];
|
||||
int unitCount = Math.Min(channel.Block.ExtensionUnit, baselineLength);
|
||||
|
||||
for (int i = 0; i < unitCount; i++)
|
||||
{
|
||||
int distance = Unpack.ReadHuffmanValue(codebook, reader, true);
|
||||
sf[i] = (baseline[i] + distance) & 31;
|
||||
}
|
||||
|
||||
for (int i = unitCount; i < channel.Block.ExtensionUnit; i++)
|
||||
{
|
||||
sf[i] = reader.ReadInt(5);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReadVlcDeltaOffsetWithBaseline(BitReader reader, Channel channel, int[] baseline, int baselineLength)
|
||||
{
|
||||
int[] sf = channel.ScaleFactors;
|
||||
int baseValue = reader.ReadOffsetBinary(5, BitReader.OffsetBias.Negative);
|
||||
int bitLength = reader.ReadInt(2) + 1;
|
||||
HuffmanCodebook codebook = Tables.HuffmanScaleFactorsUnsigned[bitLength];
|
||||
int unitCount = Math.Min(channel.Block.ExtensionUnit, baselineLength);
|
||||
|
||||
sf[0] = reader.ReadInt(bitLength);
|
||||
|
||||
for (int i = 1; i < unitCount; i++)
|
||||
{
|
||||
int delta = Unpack.ReadHuffmanValue(codebook, reader);
|
||||
sf[i] = (sf[i - 1] + delta) & (codebook.ValueMax - 1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < unitCount; i++)
|
||||
{
|
||||
sf[i] += baseValue + baseline[i];
|
||||
}
|
||||
|
||||
for (int i = unitCount; i < channel.Block.ExtensionUnit; i++)
|
||||
{
|
||||
sf[i] = reader.ReadInt(5);
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly byte[][] ScaleFactorWeights =
|
||||
{
|
||||
new byte[] {
|
||||
0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 3, 2, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 6, 6, 7, 7, 8, 10, 12, 12, 12
|
||||
}, new byte[] {
|
||||
3, 2, 2, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 2, 3, 3, 4, 5, 7, 10, 10, 10
|
||||
}, new byte[] {
|
||||
0, 2, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 8, 9, 12, 12, 12
|
||||
}, new byte[] {
|
||||
0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 8, 8, 10, 11, 11, 12, 13, 13, 13, 13
|
||||
}, new byte[] {
|
||||
0, 2, 2, 3, 3, 4, 4, 5, 4, 5, 5, 5, 5, 6, 7, 8, 8, 8, 8, 9, 9, 9, 10, 10, 11, 12, 12, 13, 13, 14, 14, 14
|
||||
}, new byte[] {
|
||||
1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 6, 7, 7, 9, 11, 11, 11
|
||||
}, new byte[] {
|
||||
0, 5, 8, 10, 11, 11, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 12, 12, 12,
|
||||
12, 13, 15, 15, 15
|
||||
}, new byte[] {
|
||||
0, 2, 3, 4, 5, 6, 6, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13,
|
||||
15, 15, 15
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
namespace LibAtrac9
|
||||
{
|
||||
internal static class Stereo
|
||||
{
|
||||
public static void ApplyIntensityStereo(Block block)
|
||||
{
|
||||
if (block.BlockType != BlockType.Stereo) return;
|
||||
|
||||
int totalUnits = block.QuantizationUnitCount;
|
||||
int stereoUnits = block.StereoQuantizationUnit;
|
||||
if (stereoUnits >= totalUnits) return;
|
||||
|
||||
Channel source = block.PrimaryChannel;
|
||||
Channel dest = block.SecondaryChannel;
|
||||
|
||||
for (int i = stereoUnits; i < totalUnits; i++)
|
||||
{
|
||||
int sign = block.JointStereoSigns[i];
|
||||
for (int sb = Tables.QuantUnitToCoeffIndex[i]; sb < Tables.QuantUnitToCoeffIndex[i + 1]; sb++)
|
||||
{
|
||||
if (sign > 0)
|
||||
{
|
||||
dest.Spectra[sb] = -source.Spectra[sb];
|
||||
}
|
||||
else
|
||||
{
|
||||
dest.Spectra[sb] = source.Spectra[sb];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
using System;
|
||||
using static LibAtrac9.HuffmanCodebooks;
|
||||
|
||||
namespace LibAtrac9
|
||||
{
|
||||
internal static class Tables
|
||||
{
|
||||
public static int MaxHuffPrecision(bool highSampleRate) => highSampleRate ? 1 : 7;
|
||||
public static int MinBandCount(bool highSampleRate) => highSampleRate ? 1 : 3;
|
||||
public static int MaxExtensionBand(bool highSampleRate) => highSampleRate ? 16 : 18;
|
||||
|
||||
public static readonly int[] SampleRates =
|
||||
{
|
||||
11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
|
||||
44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000
|
||||
};
|
||||
|
||||
public static readonly byte[] SamplingRateIndexToFrameSamplesPower = { 6, 6, 7, 7, 7, 8, 8, 8, 6, 6, 7, 7, 7, 8, 8, 8 };
|
||||
|
||||
// From sampling rate index
|
||||
public static readonly byte[] MaxBandCount = { 8, 8, 12, 12, 12, 18, 18, 18, 8, 8, 12, 12, 12, 16, 16, 16 };
|
||||
public static readonly byte[] BandToQuantUnitCount = { 0, 4, 8, 10, 12, 13, 14, 15, 16, 18, 20, 21, 22, 23, 24, 25, 26, 28, 30 };
|
||||
|
||||
public static readonly byte[] QuantUnitToCoeffCount =
|
||||
{
|
||||
02, 02, 02, 02, 02, 02, 02, 02, 04, 04, 04, 04, 08, 08, 08,
|
||||
08, 08, 08, 08, 08, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16
|
||||
};
|
||||
|
||||
public static readonly short[] QuantUnitToCoeffIndex =
|
||||
{
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
|
||||
64, 72, 80, 88, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256
|
||||
};
|
||||
|
||||
public static readonly byte[] QuantUnitToCodebookIndex =
|
||||
{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3
|
||||
};
|
||||
|
||||
public static readonly ChannelConfig[] ChannelConfig =
|
||||
{
|
||||
new ChannelConfig(BlockType.Mono),
|
||||
new ChannelConfig(BlockType.Mono, BlockType.Mono),
|
||||
new ChannelConfig(BlockType.Stereo),
|
||||
new ChannelConfig(BlockType.Stereo, BlockType.Mono, BlockType.LFE, BlockType.Stereo),
|
||||
new ChannelConfig(BlockType.Stereo, BlockType.Mono, BlockType.LFE, BlockType.Stereo, BlockType.Stereo),
|
||||
new ChannelConfig(BlockType.Stereo, BlockType.Stereo)
|
||||
};
|
||||
|
||||
public static readonly HuffmanCodebook[] HuffmanScaleFactorsUnsigned =
|
||||
GenerateHuffmanCodebooks(HuffmanScaleFactorsACodes, HuffmanScaleFactorsABits, HuffmanScaleFactorsGroupSizes);
|
||||
|
||||
public static readonly HuffmanCodebook[] HuffmanScaleFactorsSigned =
|
||||
GenerateHuffmanCodebooks(HuffmanScaleFactorsBCodes, HuffmanScaleFactorsBBits, HuffmanScaleFactorsGroupSizes);
|
||||
|
||||
public static readonly HuffmanCodebook[][][] HuffmanSpectrum =
|
||||
{
|
||||
GenerateHuffmanCodebooks(HuffmanSpectrumACodes, HuffmanSpectrumABits, HuffmanSpectrumAGroupSizes),
|
||||
GenerateHuffmanCodebooks(HuffmanSpectrumBCodes, HuffmanSpectrumBBits, HuffmanSpectrumBGroupSizes)
|
||||
};
|
||||
|
||||
public static readonly double[][] ImdctWindow = { GenerateImdctWindow(6), GenerateImdctWindow(7), GenerateImdctWindow(8) };
|
||||
|
||||
public static readonly double[] SpectrumScale = Generate(32, SpectrumScaleFunction);
|
||||
public static readonly double[] QuantizerStepSize = Generate(16, QuantizerStepSizeFunction);
|
||||
public static readonly double[] QuantizerFineStepSize = Generate(16, QuantizerFineStepSizeFunction);
|
||||
|
||||
public static readonly byte[][] GradientCurves = BitAllocation.GenerateGradientCurves();
|
||||
|
||||
private static double QuantizerStepSizeFunction(int x) => 2.0 / ((1 << (x + 1)) - 1);
|
||||
private static double QuantizerFineStepSizeFunction(int x) => QuantizerStepSizeFunction(x) / ushort.MaxValue;
|
||||
private static double SpectrumScaleFunction(int x) => Math.Pow(2, x - 15);
|
||||
|
||||
private static double[] GenerateImdctWindow(int frameSizePower)
|
||||
{
|
||||
int frameSize = 1 << frameSizePower;
|
||||
var output = new double[frameSize];
|
||||
|
||||
double[] a1 = GenerateMdctWindow(frameSizePower);
|
||||
|
||||
for (int i = 0; i < frameSize; i++)
|
||||
{
|
||||
output[i] = a1[i] / (a1[frameSize - 1 - i] * a1[frameSize - 1 - i] + a1[i] * a1[i]);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
private static double[] GenerateMdctWindow(int frameSizePower)
|
||||
{
|
||||
int frameSize = 1 << frameSizePower;
|
||||
var output = new double[frameSize];
|
||||
|
||||
for (int i = 0; i < frameSize; i++)
|
||||
{
|
||||
output[i] = (Math.Sin(((i + 0.5) / frameSize - 0.5) * Math.PI) + 1.0) * 0.5;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private static T[] Generate<T>(int count, Func<int, T> elementGenerator)
|
||||
{
|
||||
var table = new T[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
table[i] = elementGenerator(i);
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,425 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using LibAtrac9.Utilities;
|
||||
|
||||
namespace LibAtrac9
|
||||
{
|
||||
internal static class Unpack
|
||||
{
|
||||
public static void UnpackFrame(BitReader reader, Frame frame)
|
||||
{
|
||||
foreach (Block block in frame.Blocks)
|
||||
{
|
||||
UnpackBlock(reader, block);
|
||||
}
|
||||
}
|
||||
|
||||
private static void UnpackBlock(BitReader reader, Block block)
|
||||
{
|
||||
ReadBlockHeader(reader, block);
|
||||
|
||||
if (block.BlockType == BlockType.LFE)
|
||||
{
|
||||
UnpackLfeBlock(reader, block);
|
||||
}
|
||||
else
|
||||
{
|
||||
UnpackStandardBlock(reader, block);
|
||||
}
|
||||
|
||||
reader.AlignPosition(8);
|
||||
}
|
||||
|
||||
private static void ReadBlockHeader(BitReader reader, Block block)
|
||||
{
|
||||
bool firstInSuperframe = block.Frame.FrameIndex == 0;
|
||||
block.FirstInSuperframe = !reader.ReadBool();
|
||||
block.ReuseBandParams = reader.ReadBool();
|
||||
|
||||
if (block.FirstInSuperframe != firstInSuperframe)
|
||||
{
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
|
||||
if (firstInSuperframe && block.ReuseBandParams && block.BlockType != BlockType.LFE)
|
||||
{
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
}
|
||||
|
||||
private static void UnpackStandardBlock(BitReader reader, Block block)
|
||||
{
|
||||
Channel[] channels = block.Channels;
|
||||
|
||||
if (!block.ReuseBandParams)
|
||||
{
|
||||
ReadBandParams(reader, block);
|
||||
}
|
||||
|
||||
ReadGradientParams(reader, block);
|
||||
BitAllocation.CreateGradient(block);
|
||||
ReadStereoParams(reader, block);
|
||||
ReadExtensionParams(reader, block);
|
||||
|
||||
foreach (Channel channel in channels)
|
||||
{
|
||||
channel.UpdateCodedUnits();
|
||||
|
||||
ScaleFactors.Read(reader, channel);
|
||||
BitAllocation.CalculateMask(channel);
|
||||
BitAllocation.CalculatePrecisions(channel);
|
||||
CalculateSpectrumCodebookIndex(channel);
|
||||
|
||||
ReadSpectra(reader, channel);
|
||||
ReadSpectraFine(reader, channel);
|
||||
}
|
||||
|
||||
block.QuantizationUnitsPrev = block.BandExtensionEnabled ? block.ExtensionUnit : block.QuantizationUnitCount;
|
||||
}
|
||||
|
||||
private static void ReadBandParams(BitReader reader, Block block)
|
||||
{
|
||||
int minBandCount = Tables.MinBandCount(block.Config.HighSampleRate);
|
||||
int maxExtensionBand = Tables.MaxExtensionBand(block.Config.HighSampleRate);
|
||||
block.BandCount = reader.ReadInt(4);
|
||||
block.BandCount += minBandCount;
|
||||
block.QuantizationUnitCount = Tables.BandToQuantUnitCount[block.BandCount];
|
||||
if (block.BandCount < minBandCount || block.BandCount >
|
||||
Tables.MaxBandCount[block.Config.SampleRateIndex])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (block.BlockType == BlockType.Stereo)
|
||||
{
|
||||
block.StereoBand = reader.ReadInt(4);
|
||||
block.StereoBand += minBandCount;
|
||||
block.StereoQuantizationUnit = Tables.BandToQuantUnitCount[block.StereoBand];
|
||||
}
|
||||
else
|
||||
{
|
||||
block.StereoBand = block.BandCount;
|
||||
}
|
||||
|
||||
block.BandExtensionEnabled = reader.ReadBool();
|
||||
if (block.BandExtensionEnabled)
|
||||
{
|
||||
block.ExtensionBand = reader.ReadInt(4);
|
||||
block.ExtensionBand += minBandCount;
|
||||
|
||||
if (block.ExtensionBand < block.BandCount || block.ExtensionBand > maxExtensionBand)
|
||||
{
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
|
||||
block.ExtensionUnit = Tables.BandToQuantUnitCount[block.ExtensionBand];
|
||||
}
|
||||
else
|
||||
{
|
||||
block.ExtensionBand = block.BandCount;
|
||||
block.ExtensionUnit = block.QuantizationUnitCount;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReadGradientParams(BitReader reader, Block block)
|
||||
{
|
||||
block.GradientMode = reader.ReadInt(2);
|
||||
if (block.GradientMode > 0)
|
||||
{
|
||||
block.GradientEndUnit = 31;
|
||||
block.GradientEndValue = 31;
|
||||
block.GradientStartUnit = reader.ReadInt(5);
|
||||
block.GradientStartValue = reader.ReadInt(5);
|
||||
}
|
||||
else
|
||||
{
|
||||
block.GradientStartUnit = reader.ReadInt(6);
|
||||
block.GradientEndUnit = reader.ReadInt(6) + 1;
|
||||
block.GradientStartValue = reader.ReadInt(5);
|
||||
block.GradientEndValue = reader.ReadInt(5);
|
||||
}
|
||||
block.GradientBoundary = reader.ReadInt(4);
|
||||
|
||||
if (block.GradientBoundary > block.QuantizationUnitCount)
|
||||
{
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
if (block.GradientStartUnit < 1 || block.GradientStartUnit >= 48)
|
||||
{
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
if (block.GradientEndUnit < 1 || block.GradientEndUnit >= 48)
|
||||
{
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
if (block.GradientStartUnit > block.GradientEndUnit)
|
||||
{
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
if (block.GradientStartValue < 0 || block.GradientStartValue >= 32)
|
||||
{
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
if (block.GradientEndValue < 0 || block.GradientEndValue >= 32)
|
||||
{
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReadStereoParams(BitReader reader, Block block)
|
||||
{
|
||||
if (block.BlockType != BlockType.Stereo) return;
|
||||
|
||||
block.PrimaryChannelIndex = reader.ReadInt(1);
|
||||
block.HasJointStereoSigns = reader.ReadBool();
|
||||
if (block.HasJointStereoSigns)
|
||||
{
|
||||
for (int i = block.StereoQuantizationUnit; i < block.QuantizationUnitCount; i++)
|
||||
{
|
||||
block.JointStereoSigns[i] = reader.ReadInt(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Clear(block.JointStereoSigns, 0, block.JointStereoSigns.Length);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReadExtensionParams(BitReader reader, Block block)
|
||||
{
|
||||
// ReSharper disable once RedundantAssignment
|
||||
int bexBand = 0;
|
||||
if (block.BandExtensionEnabled)
|
||||
{
|
||||
BandExtension.GetBexBandInfo(out bexBand, out _, out _, block.QuantizationUnitCount);
|
||||
if (block.BlockType == BlockType.Stereo)
|
||||
{
|
||||
ReadHeader(block.Channels[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.Position += 1;
|
||||
}
|
||||
}
|
||||
block.HasExtensionData = reader.ReadBool();
|
||||
|
||||
if (!block.HasExtensionData) return;
|
||||
if (!block.BandExtensionEnabled)
|
||||
{
|
||||
block.BexMode = reader.ReadInt(2);
|
||||
block.BexDataLength = reader.ReadInt(5);
|
||||
reader.Position += block.BexDataLength;
|
||||
return;
|
||||
}
|
||||
|
||||
ReadHeader(block.Channels[0]);
|
||||
|
||||
block.BexDataLength = reader.ReadInt(5);
|
||||
if (block.BexDataLength <= 0) return;
|
||||
int bexDataEnd = reader.Position + block.BexDataLength;
|
||||
|
||||
ReadData(block.Channels[0]);
|
||||
|
||||
if (block.BlockType == BlockType.Stereo)
|
||||
{
|
||||
ReadData(block.Channels[1]);
|
||||
}
|
||||
|
||||
// Make sure we didn't read too many bits
|
||||
if (reader.Position > bexDataEnd)
|
||||
{
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
|
||||
void ReadHeader(Channel channel)
|
||||
{
|
||||
int bexMode = reader.ReadInt(2);
|
||||
channel.BexMode = bexBand > 2 ? bexMode : 4;
|
||||
channel.BexValueCount = BandExtension.BexEncodedValueCounts[channel.BexMode][bexBand];
|
||||
}
|
||||
|
||||
void ReadData(Channel channel)
|
||||
{
|
||||
for (int i = 0; i < channel.BexValueCount; i++)
|
||||
{
|
||||
int dataLength = BandExtension.BexDataLengths[channel.BexMode][bexBand][i];
|
||||
channel.BexValues[i] = reader.ReadInt(dataLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void CalculateSpectrumCodebookIndex(Channel channel)
|
||||
{
|
||||
Array.Clear(channel.CodebookSet, 0, channel.CodebookSet.Length);
|
||||
int quantUnits = channel.CodedQuantUnits;
|
||||
int[] sf = channel.ScaleFactors;
|
||||
|
||||
if (quantUnits <= 1) return;
|
||||
if (channel.Config.HighSampleRate) return;
|
||||
|
||||
// Temporarily setting this value allows for simpler code by
|
||||
// making the last value a non-special case.
|
||||
int originalScaleTmp = sf[quantUnits];
|
||||
sf[quantUnits] = sf[quantUnits - 1];
|
||||
|
||||
int avg = 0;
|
||||
if (quantUnits > 12)
|
||||
{
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
avg += sf[i];
|
||||
}
|
||||
avg = (avg + 6) / 12;
|
||||
}
|
||||
|
||||
for (int i = 8; i < quantUnits; i++)
|
||||
{
|
||||
int prevSf = sf[i - 1];
|
||||
int nextSf = sf[i + 1];
|
||||
int minSf = Math.Min(prevSf, nextSf);
|
||||
if (sf[i] - minSf >= 3 || sf[i] - prevSf + sf[i] - nextSf >= 3)
|
||||
{
|
||||
channel.CodebookSet[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 12; i < quantUnits; i++)
|
||||
{
|
||||
if (channel.CodebookSet[i] == 0)
|
||||
{
|
||||
int minSf = Math.Min(sf[i - 1], sf[i + 1]);
|
||||
if (sf[i] - minSf >= 2 && sf[i] >= avg - (Tables.QuantUnitToCoeffCount[i] == 16 ? 1 : 0))
|
||||
{
|
||||
channel.CodebookSet[i] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sf[quantUnits] = originalScaleTmp;
|
||||
}
|
||||
|
||||
private static void ReadSpectra(BitReader reader, Channel channel)
|
||||
{
|
||||
int[] values = channel.SpectraValuesBuffer;
|
||||
Array.Clear(channel.QuantizedSpectra, 0, channel.QuantizedSpectra.Length);
|
||||
int maxHuffPrecision = Tables.MaxHuffPrecision(channel.Config.HighSampleRate);
|
||||
|
||||
for (int i = 0; i < channel.CodedQuantUnits; i++)
|
||||
{
|
||||
int subbandCount = Tables.QuantUnitToCoeffCount[i];
|
||||
int precision = channel.Precisions[i] + 1;
|
||||
if (precision <= maxHuffPrecision)
|
||||
{
|
||||
HuffmanCodebook huff = Tables.HuffmanSpectrum[channel.CodebookSet[i]][precision][Tables.QuantUnitToCodebookIndex[i]];
|
||||
int groupCount = subbandCount >> huff.ValueCountPower;
|
||||
for (int j = 0; j < groupCount; j++)
|
||||
{
|
||||
values[j] = ReadHuffmanValue(huff, reader);
|
||||
}
|
||||
|
||||
DecodeHuffmanValues(channel.QuantizedSpectra, Tables.QuantUnitToCoeffIndex[i], subbandCount, huff, values);
|
||||
}
|
||||
else
|
||||
{
|
||||
int subbandIndex = Tables.QuantUnitToCoeffIndex[i];
|
||||
for (int j = subbandIndex; j < Tables.QuantUnitToCoeffIndex[i + 1]; j++)
|
||||
{
|
||||
channel.QuantizedSpectra[j] = reader.ReadSignedInt(precision);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReadSpectraFine(BitReader reader, Channel channel)
|
||||
{
|
||||
Array.Clear(channel.QuantizedSpectraFine, 0, channel.QuantizedSpectraFine.Length);
|
||||
|
||||
for (int i = 0; i < channel.CodedQuantUnits; i++)
|
||||
{
|
||||
if (channel.PrecisionsFine[i] > 0)
|
||||
{
|
||||
int overflowBits = channel.PrecisionsFine[i] + 1;
|
||||
int startSubband = Tables.QuantUnitToCoeffIndex[i];
|
||||
int endSubband = Tables.QuantUnitToCoeffIndex[i + 1];
|
||||
|
||||
for (int j = startSubband; j < endSubband; j++)
|
||||
{
|
||||
channel.QuantizedSpectraFine[j] = reader.ReadSignedInt(overflowBits);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void DecodeHuffmanValues(int[] spectrum, int index, int bandCount, HuffmanCodebook huff, int[] values)
|
||||
{
|
||||
int valueCount = bandCount >> huff.ValueCountPower;
|
||||
int mask = (1 << huff.ValueBits) - 1;
|
||||
|
||||
for (int i = 0; i < valueCount; i++)
|
||||
{
|
||||
int value = values[i];
|
||||
for (int j = 0; j < huff.ValueCount; j++)
|
||||
{
|
||||
spectrum[index++] = Bit.SignExtend32(value & mask, huff.ValueBits);
|
||||
value >>= huff.ValueBits;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int ReadHuffmanValue(HuffmanCodebook huff, BitReader reader, bool signed = false)
|
||||
{
|
||||
int code = reader.PeekInt(huff.MaxBitSize);
|
||||
byte value = huff.Lookup[code];
|
||||
int bits = huff.Bits[value];
|
||||
reader.Position += bits;
|
||||
return signed ? Bit.SignExtend32(value, huff.ValueBits) : value;
|
||||
}
|
||||
|
||||
private static void UnpackLfeBlock(BitReader reader, Block block)
|
||||
{
|
||||
Channel channel = block.Channels[0];
|
||||
block.QuantizationUnitCount = 2;
|
||||
|
||||
DecodeLfeScaleFactors(reader, channel);
|
||||
CalculateLfePrecision(channel);
|
||||
channel.CodedQuantUnits = block.QuantizationUnitCount;
|
||||
ReadLfeSpectra(reader, channel);
|
||||
}
|
||||
|
||||
private static void DecodeLfeScaleFactors(BitReader reader, Channel channel)
|
||||
{
|
||||
Array.Clear(channel.ScaleFactors, 0, channel.ScaleFactors.Length);
|
||||
for (int i = 0; i < channel.Block.QuantizationUnitCount; i++)
|
||||
{
|
||||
channel.ScaleFactors[i] = reader.ReadInt(5);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CalculateLfePrecision(Channel channel)
|
||||
{
|
||||
Block block = channel.Block;
|
||||
int precision = block.ReuseBandParams ? 8 : 4;
|
||||
for (int i = 0; i < block.QuantizationUnitCount; i++)
|
||||
{
|
||||
channel.Precisions[i] = precision;
|
||||
channel.PrecisionsFine[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReadLfeSpectra(BitReader reader, Channel channel)
|
||||
{
|
||||
Array.Clear(channel.QuantizedSpectra, 0, channel.QuantizedSpectra.Length);
|
||||
|
||||
for (int i = 0; i < channel.CodedQuantUnits; i++)
|
||||
{
|
||||
if (channel.Precisions[i] <= 0) continue;
|
||||
|
||||
int precision = channel.Precisions[i] + 1;
|
||||
for (int j = Tables.QuantUnitToCoeffIndex[i]; j < Tables.QuantUnitToCoeffIndex[i + 1]; j++)
|
||||
{
|
||||
channel.QuantizedSpectra[j] = reader.ReadSignedInt(precision);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
namespace LibAtrac9.Utilities
|
||||
{
|
||||
internal static class Bit
|
||||
{
|
||||
private static uint BitReverse32(uint value)
|
||||
{
|
||||
value = ((value & 0xaaaaaaaa) >> 1) | ((value & 0x55555555) << 1);
|
||||
value = ((value & 0xcccccccc) >> 2) | ((value & 0x33333333) << 2);
|
||||
value = ((value & 0xf0f0f0f0) >> 4) | ((value & 0x0f0f0f0f) << 4);
|
||||
value = ((value & 0xff00ff00) >> 8) | ((value & 0x00ff00ff) << 8);
|
||||
return (value >> 16) | (value << 16);
|
||||
}
|
||||
private static uint BitReverse32(uint value, int bitCount) => BitReverse32(value) >> (32 - bitCount);
|
||||
public static int BitReverse32(int value, int bitCount) => (int) BitReverse32((uint) value, bitCount);
|
||||
|
||||
public static int SignExtend32(int value, int bits)
|
||||
{
|
||||
int shift = 8 * sizeof(int) - bits;
|
||||
return (value << shift) >> shift;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace LibAtrac9.Utilities
|
||||
{
|
||||
internal class BitReader
|
||||
{
|
||||
private byte[] Buffer { get; set; }
|
||||
private int LengthBits { get; set; }
|
||||
public int Position { get; set; }
|
||||
private int Remaining => LengthBits - Position;
|
||||
|
||||
public BitReader(byte[] buffer) => SetBuffer(buffer);
|
||||
|
||||
public void SetBuffer(byte[] buffer)
|
||||
{
|
||||
Buffer = buffer;
|
||||
LengthBits = Buffer?.Length * 8 ?? 0;
|
||||
Position = 0;
|
||||
}
|
||||
|
||||
public int ReadInt(int bitCount)
|
||||
{
|
||||
int value = PeekInt(bitCount);
|
||||
Position += bitCount;
|
||||
return value;
|
||||
}
|
||||
|
||||
public int ReadSignedInt(int bitCount)
|
||||
{
|
||||
int value = PeekInt(bitCount);
|
||||
Position += bitCount;
|
||||
return Bit.SignExtend32(value, bitCount);
|
||||
}
|
||||
|
||||
public bool ReadBool() => ReadInt(1) == 1;
|
||||
|
||||
public int ReadOffsetBinary(int bitCount, OffsetBias bias)
|
||||
{
|
||||
int offset = (1 << (bitCount - 1)) - (int)bias;
|
||||
int value = PeekInt(bitCount) - offset;
|
||||
Position += bitCount;
|
||||
return value;
|
||||
}
|
||||
|
||||
public void AlignPosition(int multiple)
|
||||
{
|
||||
Position = Helpers.GetNextMultiple(Position, multiple);
|
||||
}
|
||||
|
||||
public int PeekInt(int bitCount)
|
||||
{
|
||||
Debug.Assert(bitCount >= 0 && bitCount <= 32);
|
||||
|
||||
if (bitCount > Remaining)
|
||||
{
|
||||
if (Position >= LengthBits) return 0;
|
||||
|
||||
int extraBits = bitCount - Remaining;
|
||||
return PeekIntFallback(Remaining) << extraBits;
|
||||
}
|
||||
|
||||
int byteIndex = Position / 8;
|
||||
int bitIndex = Position % 8;
|
||||
|
||||
if (bitCount <= 9 && Remaining >= 16)
|
||||
{
|
||||
int value = Buffer[byteIndex] << 8 | Buffer[byteIndex + 1];
|
||||
value &= 0xFFFF >> bitIndex;
|
||||
value >>= 16 - bitCount - bitIndex;
|
||||
return value;
|
||||
}
|
||||
|
||||
if (bitCount <= 17 && Remaining >= 24)
|
||||
{
|
||||
int value = Buffer[byteIndex] << 16 | Buffer[byteIndex + 1] << 8 | Buffer[byteIndex + 2];
|
||||
value &= 0xFFFFFF >> bitIndex;
|
||||
value >>= 24 - bitCount - bitIndex;
|
||||
return value;
|
||||
}
|
||||
|
||||
if (bitCount <= 25 && Remaining >= 32)
|
||||
{
|
||||
int value = Buffer[byteIndex] << 24 | Buffer[byteIndex + 1] << 16 | Buffer[byteIndex + 2] << 8 | Buffer[byteIndex + 3];
|
||||
value &= (int)(0xFFFFFFFF >> bitIndex);
|
||||
value >>= 32 - bitCount - bitIndex;
|
||||
return value;
|
||||
}
|
||||
return PeekIntFallback(bitCount);
|
||||
}
|
||||
|
||||
private int PeekIntFallback(int bitCount)
|
||||
{
|
||||
int value = 0;
|
||||
int byteIndex = Position / 8;
|
||||
int bitIndex = Position % 8;
|
||||
|
||||
while (bitCount > 0)
|
||||
{
|
||||
if (bitIndex >= 8)
|
||||
{
|
||||
bitIndex = 0;
|
||||
byteIndex++;
|
||||
}
|
||||
|
||||
int bitsToRead = Math.Min(bitCount, 8 - bitIndex);
|
||||
int mask = 0xFF >> bitIndex;
|
||||
int currentByte = (mask & Buffer[byteIndex]) >> (8 - bitIndex - bitsToRead);
|
||||
|
||||
value = (value << bitsToRead) | currentByte;
|
||||
bitIndex += bitsToRead;
|
||||
bitCount -= bitsToRead;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the bias of an offset binary value. A positive bias can represent one more
|
||||
/// positive value than negative value, and a negative bias can represent one more
|
||||
/// negative value than positive value.
|
||||
/// </summary>
|
||||
/// <remarks>Example:
|
||||
/// A 4-bit offset binary value with a positive bias can store
|
||||
/// the values 8 through -7 inclusive.
|
||||
/// A 4-bit offset binary value with a positive bias can store
|
||||
/// the values 7 through -8 inclusive.</remarks>
|
||||
public enum OffsetBias
|
||||
{
|
||||
Negative = 0
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace LibAtrac9.Utilities
|
||||
{
|
||||
internal static class Helpers
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static short Clamp16(int value)
|
||||
{
|
||||
if (value > short.MaxValue)
|
||||
return short.MaxValue;
|
||||
if (value < short.MinValue)
|
||||
return short.MinValue;
|
||||
return (short)value;
|
||||
}
|
||||
|
||||
public static int GetNextMultiple(int value, int multiple)
|
||||
{
|
||||
if (multiple <= 0)
|
||||
return value;
|
||||
|
||||
if (value % multiple == 0)
|
||||
return value;
|
||||
|
||||
return value + multiple - value % multiple;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the floor of the base 2 logarithm of a specified number.
|
||||
/// </summary>
|
||||
/// <param name="value">The number whose logarithm is to be found.</param>
|
||||
/// <returns>The floor of the base 2 logarithm of <paramref name="value"/>.</returns>
|
||||
public static int Log2(int value)
|
||||
{
|
||||
value |= value >> 1;
|
||||
value |= value >> 2;
|
||||
value |= value >> 4;
|
||||
value |= value >> 8;
|
||||
value |= value >> 16;
|
||||
|
||||
return MultiplyDeBruijnBitPosition[(uint)(value * 0x07C4ACDDU) >> 27];
|
||||
}
|
||||
|
||||
private static readonly int[] MultiplyDeBruijnBitPosition =
|
||||
{
|
||||
0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
|
||||
8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace LibAtrac9.Utilities
|
||||
{
|
||||
internal class Mdct
|
||||
{
|
||||
private int MdctBits { get; }
|
||||
private int MdctSize { get; }
|
||||
private double Scale { get; }
|
||||
|
||||
private static readonly object TableLock = new object();
|
||||
private static int _tableBits = -1;
|
||||
private static readonly List<double[]> SinTables = new List<double[]>();
|
||||
private static readonly List<double[]> CosTables = new List<double[]>();
|
||||
private static readonly List<int[]> ShuffleTables = new List<int[]>();
|
||||
|
||||
private readonly double[] _imdctPrevious;
|
||||
private readonly double[] _imdctWindow;
|
||||
|
||||
private readonly double[] _scratchMdct;
|
||||
private readonly double[] _scratchDct;
|
||||
|
||||
public Mdct(int mdctBits, double[] window, double scale = 1)
|
||||
{
|
||||
SetTables(mdctBits);
|
||||
|
||||
MdctBits = mdctBits;
|
||||
MdctSize = 1 << mdctBits;
|
||||
Scale = scale;
|
||||
|
||||
if (window.Length < MdctSize)
|
||||
{
|
||||
throw new ArgumentException("Window must be as long as the MDCT size.", nameof(window));
|
||||
}
|
||||
|
||||
_imdctPrevious = new double[MdctSize];
|
||||
_scratchMdct = new double[MdctSize];
|
||||
_scratchDct = new double[MdctSize];
|
||||
_imdctWindow = window;
|
||||
}
|
||||
|
||||
private static void SetTables(int maxBits)
|
||||
{
|
||||
lock (TableLock)
|
||||
{
|
||||
if (maxBits > _tableBits)
|
||||
{
|
||||
for (int i = _tableBits + 1; i <= maxBits; i++)
|
||||
{
|
||||
GenerateTrigTables(i, out double[] sin, out double[] cos);
|
||||
SinTables.Add(sin);
|
||||
CosTables.Add(cos);
|
||||
ShuffleTables.Add(GenerateShuffleTable(i));
|
||||
}
|
||||
_tableBits = maxBits;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RunImdct(double[] input, double[] output)
|
||||
{
|
||||
if (input.Length < MdctSize)
|
||||
{
|
||||
throw new ArgumentException("Input must be as long as the MDCT size.", nameof(input));
|
||||
}
|
||||
|
||||
if (output.Length < MdctSize)
|
||||
{
|
||||
throw new ArgumentException("Output must be as long as the MDCT size.", nameof(output));
|
||||
}
|
||||
|
||||
int size = MdctSize;
|
||||
int half = size / 2;
|
||||
double[] dctOut = _scratchMdct;
|
||||
|
||||
Dct4(input, dctOut);
|
||||
|
||||
for (int i = 0; i < half; i++)
|
||||
{
|
||||
output[i] = _imdctWindow[i] * dctOut[i + half] + _imdctPrevious[i];
|
||||
output[i + half] = _imdctWindow[i + half] * -dctOut[size - 1 - i] - _imdctPrevious[i + half];
|
||||
_imdctPrevious[i] = _imdctWindow[size - 1 - i] * -dctOut[half - i - 1];
|
||||
_imdctPrevious[i + half] = _imdctWindow[half - i - 1] * dctOut[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does a Type-4 DCT.
|
||||
/// </summary>
|
||||
/// <param name="input">The input array containing the time or frequency-domain samples</param>
|
||||
/// <param name="output">The output array that will contain the transformed time or frequency-domain samples</param>
|
||||
private void Dct4(double[] input, double[] output)
|
||||
{
|
||||
int[] shuffleTable = ShuffleTables[MdctBits];
|
||||
double[] sinTable = SinTables[MdctBits];
|
||||
double[] cosTable = CosTables[MdctBits];
|
||||
double[] dctTemp = _scratchDct;
|
||||
|
||||
int size = MdctSize;
|
||||
int lastIndex = size - 1;
|
||||
int halfSize = size / 2;
|
||||
|
||||
for (int i = 0; i < halfSize; i++)
|
||||
{
|
||||
int i2 = i * 2;
|
||||
double a = input[i2];
|
||||
double b = input[lastIndex - i2];
|
||||
double sin = sinTable[i];
|
||||
double cos = cosTable[i];
|
||||
dctTemp[i2] = a * cos + b * sin;
|
||||
dctTemp[i2 + 1] = a * sin - b * cos;
|
||||
}
|
||||
int stageCount = MdctBits - 1;
|
||||
|
||||
for (int stage = 0; stage < stageCount; stage++)
|
||||
{
|
||||
int blockCount = 1 << stage;
|
||||
int blockSizeBits = stageCount - stage;
|
||||
int blockHalfSizeBits = blockSizeBits - 1;
|
||||
int blockSize = 1 << blockSizeBits;
|
||||
int blockHalfSize = 1 << blockHalfSizeBits;
|
||||
sinTable = SinTables[blockHalfSizeBits];
|
||||
cosTable = CosTables[blockHalfSizeBits];
|
||||
|
||||
for (int block = 0; block < blockCount; block++)
|
||||
{
|
||||
for (int i = 0; i < blockHalfSize; i++)
|
||||
{
|
||||
int frontPos = (block * blockSize + i) * 2;
|
||||
int backPos = frontPos + blockSize;
|
||||
double a = dctTemp[frontPos] - dctTemp[backPos];
|
||||
double b = dctTemp[frontPos + 1] - dctTemp[backPos + 1];
|
||||
double sin = sinTable[i];
|
||||
double cos = cosTable[i];
|
||||
dctTemp[frontPos] += dctTemp[backPos];
|
||||
dctTemp[frontPos + 1] += dctTemp[backPos + 1];
|
||||
dctTemp[backPos] = a * cos + b * sin;
|
||||
dctTemp[backPos + 1] = a * sin - b * cos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < MdctSize; i++)
|
||||
{
|
||||
output[i] = dctTemp[shuffleTable[i]] * Scale;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void GenerateTrigTables(int sizeBits, out double[] sin, out double[] cos)
|
||||
{
|
||||
int size = 1 << sizeBits;
|
||||
sin = new double[size];
|
||||
cos = new double[size];
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
double value = Math.PI * (4 * i + 1) / (4 * size);
|
||||
sin[i] = Math.Sin(value);
|
||||
cos[i] = Math.Cos(value);
|
||||
}
|
||||
}
|
||||
|
||||
internal static int[] GenerateShuffleTable(int sizeBits)
|
||||
{
|
||||
int size = 1 << sizeBits;
|
||||
var table = new int[size];
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
table[i] = Bit.BitReverse32(i ^ (i / 2), sizeBits);
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,6 +12,11 @@
|
|||
8301659B1F256BD000CA0941 /* ea_schl_fixed.c in Sources */ = {isa = PBXBuildFile; fileRef = 830165981F256BD000CA0941 /* ea_schl_fixed.c */; };
|
||||
8301659C1F256BD000CA0941 /* nds_strm_ffta2.c in Sources */ = {isa = PBXBuildFile; fileRef = 830165991F256BD000CA0941 /* nds_strm_ffta2.c */; };
|
||||
830165A21F256BF400CA0941 /* hwas_blocked.c in Sources */ = {isa = PBXBuildFile; fileRef = 830165A11F256BF400CA0941 /* hwas_blocked.c */; };
|
||||
830EBE102004655D0023AA10 /* atrac9_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBE0F2004655D0023AA10 /* atrac9_decoder.c */; };
|
||||
830EBE132004656E0023AA10 /* xnb.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBE112004656E0023AA10 /* xnb.c */; };
|
||||
830EBE142004656E0023AA10 /* ktss.c in Sources */ = {isa = PBXBuildFile; fileRef = 830EBE122004656E0023AA10 /* ktss.c */; };
|
||||
830EBE19200465B00023AA10 /* libatrac9.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 830EBD9720045F1B0023AA10 /* libatrac9.framework */; };
|
||||
830EBE1A200465C00023AA10 /* libatrac9.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 830EBD9720045F1B0023AA10 /* libatrac9.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
8313E3E61902020400B4B6F1 /* mpg123.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8313E3431901FBDD00B4B6F1 /* mpg123.framework */; };
|
||||
8313E3E71902021800B4B6F1 /* mpg123.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8313E3431901FBDD00B4B6F1 /* mpg123.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
831BA6181EAC61A500CF89B0 /* adx.c in Sources */ = {isa = PBXBuildFile; fileRef = 831BA60E1EAC61A500CF89B0 /* adx.c */; };
|
||||
|
@ -444,6 +449,27 @@
|
|||
/* End PBXBuildRule section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
830EBD9620045F1B0023AA10 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 830EBD9220045F190023AA10 /* libatrac9.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 830EBD8720045F190023AA10;
|
||||
remoteInfo = libatrac9;
|
||||
};
|
||||
830EBE152004659B0023AA10 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 830EBD9220045F190023AA10 /* libatrac9.xcodeproj */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 830EBD8620045F190023AA10;
|
||||
remoteInfo = libatrac9;
|
||||
};
|
||||
830EBE17200465A30023AA10 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 83F412871E932F9A002E37D0 /* Vorbis.xcodeproj */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 730F23A1091827B100AB638C;
|
||||
remoteInfo = Vorbis;
|
||||
};
|
||||
8313E3421901FBDD00B4B6F1 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 8313E33D1901FBDC00B4B6F1 /* mpg123.xcodeproj */;
|
||||
|
@ -523,6 +549,7 @@
|
|||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
830EBE1A200465C00023AA10 /* libatrac9.framework in CopyFiles */,
|
||||
832C70BF1E9335E400BD7B4E /* Vorbis.framework in CopyFiles */,
|
||||
83D7318A1A749D2200CA1366 /* g719.framework in CopyFiles */,
|
||||
83D731111A7394D300CA1366 /* g7221.framework in CopyFiles */,
|
||||
|
@ -538,6 +565,10 @@
|
|||
830165981F256BD000CA0941 /* ea_schl_fixed.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_schl_fixed.c; sourceTree = "<group>"; };
|
||||
830165991F256BD000CA0941 /* nds_strm_ffta2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nds_strm_ffta2.c; sourceTree = "<group>"; };
|
||||
830165A11F256BF400CA0941 /* hwas_blocked.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hwas_blocked.c; sourceTree = "<group>"; };
|
||||
830EBD9220045F190023AA10 /* libatrac9.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = libatrac9.xcodeproj; path = ../libatrac9/libatrac9.xcodeproj; sourceTree = "<group>"; };
|
||||
830EBE0F2004655D0023AA10 /* atrac9_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = atrac9_decoder.c; sourceTree = "<group>"; };
|
||||
830EBE112004656E0023AA10 /* xnb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xnb.c; sourceTree = "<group>"; };
|
||||
830EBE122004656E0023AA10 /* ktss.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ktss.c; sourceTree = "<group>"; };
|
||||
8313E33D1901FBDC00B4B6F1 /* mpg123.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = mpg123.xcodeproj; path = ../mpg123/mpg123.xcodeproj; sourceTree = "<group>"; };
|
||||
831BA60E1EAC61A500CF89B0 /* adx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = adx.c; sourceTree = "<group>"; };
|
||||
831BA60F1EAC61A500CF89B0 /* ogl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ogl.c; sourceTree = "<group>"; };
|
||||
|
@ -960,6 +991,7 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
830EBE19200465B00023AA10 /* libatrac9.framework in Frameworks */,
|
||||
838BDB7F1D3B1FD10022CA6F /* Cocoa.framework in Frameworks */,
|
||||
838BDB7D1D3B1FCC0022CA6F /* CoreVideo.framework in Frameworks */,
|
||||
838BDB7B1D3B1FC20022CA6F /* CoreMedia.framework in Frameworks */,
|
||||
|
@ -984,6 +1016,14 @@
|
|||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
830EBD9320045F190023AA10 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
830EBD9720045F1B0023AA10 /* libatrac9.framework */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8313E33E1901FBDC00B4B6F1 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1040,6 +1080,7 @@
|
|||
836F6B3E18BDB8880095E648 /* Other Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
830EBD9220045F190023AA10 /* libatrac9.xcodeproj */,
|
||||
838BDB611D3AF08C0022CA6F /* libavcodec.a */,
|
||||
838BDB621D3AF08C0022CA6F /* libavformat.a */,
|
||||
838BDB631D3AF08C0022CA6F /* libavutil.a */,
|
||||
|
@ -1093,6 +1134,7 @@
|
|||
836F6DDF18BDC2180095E648 /* coding */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
830EBE0F2004655D0023AA10 /* atrac9_decoder.c */,
|
||||
8349A8DE1FE6251F00E26435 /* ea_mt_decoder.c */,
|
||||
8349A8DD1FE6251F00E26435 /* ffmpeg_decoder_utils_bgw_atrac3.c */,
|
||||
8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */,
|
||||
|
@ -1204,6 +1246,8 @@
|
|||
836F6E2718BDC2180095E648 /* meta */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
830EBE122004656E0023AA10 /* ktss.c */,
|
||||
830EBE112004656E0023AA10 /* xnb.c */,
|
||||
8349A9041FE6258100E26435 /* aax_streamfile.h */,
|
||||
8349A9021FE6258100E26435 /* adx_keys.h */,
|
||||
8349A9001FE6258000E26435 /* afc.c */,
|
||||
|
@ -1576,6 +1620,8 @@
|
|||
48C265101A5D424500A0A3D6 /* PBXBuildRule */,
|
||||
);
|
||||
dependencies = (
|
||||
830EBE18200465A30023AA10 /* PBXTargetDependency */,
|
||||
830EBE162004659B0023AA10 /* PBXTargetDependency */,
|
||||
83D731881A749D0D00CA1366 /* PBXTargetDependency */,
|
||||
83D7310F1A7394B500CA1366 /* PBXTargetDependency */,
|
||||
8313E3E91902021F00B4B6F1 /* PBXTargetDependency */,
|
||||
|
@ -1619,6 +1665,10 @@
|
|||
ProductGroup = 83D730E61A738EB200CA1366 /* Products */;
|
||||
ProjectRef = 83D730E51A738EB200CA1366 /* g7221.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 830EBD9320045F190023AA10 /* Products */;
|
||||
ProjectRef = 830EBD9220045F190023AA10 /* libatrac9.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 8313E33E1901FBDC00B4B6F1 /* Products */;
|
||||
ProjectRef = 8313E33D1901FBDC00B4B6F1 /* mpg123.xcodeproj */;
|
||||
|
@ -1636,6 +1686,13 @@
|
|||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXReferenceProxy section */
|
||||
830EBD9720045F1B0023AA10 /* libatrac9.framework */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.framework;
|
||||
path = libatrac9.framework;
|
||||
remoteRef = 830EBD9620045F1B0023AA10 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
8313E3431901FBDD00B4B6F1 /* mpg123.framework */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.framework;
|
||||
|
@ -1781,6 +1838,7 @@
|
|||
8349A91F1FE6258200E26435 /* naac.c in Sources */,
|
||||
836F6FD218BDC2190095E648 /* ps2_bmdx.c in Sources */,
|
||||
836F703C18BDC2190095E648 /* wii_bns.c in Sources */,
|
||||
830EBE132004656E0023AA10 /* xnb.c in Sources */,
|
||||
835027131ED119E000C25929 /* mta2_decoder.c in Sources */,
|
||||
836F6FA718BDC2190095E648 /* nds_strm.c in Sources */,
|
||||
8349A91A1FE6258200E26435 /* vxn.c in Sources */,
|
||||
|
@ -1817,8 +1875,10 @@
|
|||
836F701118BDC2190095E648 /* ps3_cps.c in Sources */,
|
||||
836F701418BDC2190095E648 /* ps3_msf.c in Sources */,
|
||||
836F6F6518BDC2190095E648 /* 2dx9.c in Sources */,
|
||||
830EBE102004655D0023AA10 /* atrac9_decoder.c in Sources */,
|
||||
836F700818BDC2190095E648 /* ps2_vms.c in Sources */,
|
||||
836F702418BDC2190095E648 /* rwsd.c in Sources */,
|
||||
830EBE142004656E0023AA10 /* ktss.c in Sources */,
|
||||
836F6F5618BDC2190095E648 /* scd_int_layout.c in Sources */,
|
||||
836F6F6618BDC2190095E648 /* aax.c in Sources */,
|
||||
836F6FD618BDC2190095E648 /* ps2_exst.c in Sources */,
|
||||
|
@ -2084,6 +2144,16 @@
|
|||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
830EBE162004659B0023AA10 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
name = libatrac9;
|
||||
targetProxy = 830EBE152004659B0023AA10 /* PBXContainerItemProxy */;
|
||||
};
|
||||
830EBE18200465A30023AA10 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
name = Vorbis;
|
||||
targetProxy = 830EBE17200465A30023AA10 /* PBXContainerItemProxy */;
|
||||
};
|
||||
8313E3E91902021F00B4B6F1 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
name = mpg123;
|
||||
|
|
|
@ -4,6 +4,20 @@
|
|||
#ifdef VGM_USE_MAIATRAC3PLUS
|
||||
#include "maiatrac3plus.h"
|
||||
|
||||
maiatrac3plus_codec_data *init_at3plus() {
|
||||
|
||||
maiatrac3plus_codec_data *data = malloc(sizeof(maiatrac3plus_codec_data));
|
||||
data->buffer = 0;
|
||||
data->samples_discard = 0;
|
||||
data->handle = Atrac3plusDecoder_openContext();
|
||||
if (!data->handle) goto fail;
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void decode_at3plus(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel) {
|
||||
VGMSTREAMCHANNEL *ch = &vgmstream->ch[0];
|
||||
maiatrac3plus_codec_data *data = vgmstream->codec_data;
|
||||
|
@ -25,8 +39,7 @@ void decode_at3plus(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing,
|
|||
data->samples_discard = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < samples_to_do; i++)
|
||||
{
|
||||
for (i = 0; i < samples_to_do; i++) {
|
||||
outbuf[i*channelspacing] = data->buffer[(first_sample+i)*data->channels+channel];
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,219 @@
|
|||
#include "coding.h"
|
||||
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
#include <libatrac9/libatrac9.h>
|
||||
|
||||
|
||||
atrac9_codec_data *init_atrac9(atrac9_config *cfg) {
|
||||
int status;
|
||||
uint8_t config_data[4];
|
||||
Atrac9CodecInfo info = {0};
|
||||
atrac9_codec_data *data = NULL;
|
||||
|
||||
data = calloc(1, sizeof(atrac9_codec_data));
|
||||
|
||||
data->handle = Atrac9GetHandle();
|
||||
if (!data->handle) goto fail;
|
||||
|
||||
put_32bitBE(config_data, cfg->config_data);
|
||||
status = Atrac9InitDecoder(data->handle, config_data);
|
||||
if (status < 0) goto fail;
|
||||
|
||||
status = Atrac9GetCodecInfo(data->handle, &info);
|
||||
if (status < 0) goto fail;
|
||||
//;VGM_LOG("ATRAC9: config=%x, sf-size=%x, sub-frames=%i x %i samples\n", cfg->config_data, info.superframeSize, info.framesInSuperframe, info.frameSamples);
|
||||
|
||||
if (cfg->channels && cfg->channels != info.channels) {
|
||||
VGM_LOG("ATRAC9: channels in header %i vs config %i don't match\n", cfg->channels, info.channels);
|
||||
goto fail; /* unknown multichannel layout */
|
||||
}
|
||||
|
||||
|
||||
/* must hold at least one superframe and its samples */
|
||||
data->data_buffer_size = info.superframeSize;
|
||||
data->data_buffer = calloc(sizeof(uint8_t), data->data_buffer_size);
|
||||
data->sample_buffer = calloc(sizeof(sample), info.channels * info.frameSamples * info.framesInSuperframe);
|
||||
|
||||
data->samples_to_discard = cfg->encoder_delay;
|
||||
|
||||
memcpy(&data->config, cfg, sizeof(atrac9_config));
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void decode_atrac9(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, int channels) {
|
||||
VGMSTREAMCHANNEL *stream = &vgmstream->ch[0];
|
||||
atrac9_codec_data * data = vgmstream->codec_data;
|
||||
int samples_done = 0;
|
||||
|
||||
|
||||
while (samples_done < samples_to_do) {
|
||||
|
||||
if (data->samples_filled) { /* consume samples */
|
||||
int samples_to_get = data->samples_filled;
|
||||
|
||||
if (data->samples_to_discard) {
|
||||
/* discard samples for looping */
|
||||
if (samples_to_get > data->samples_to_discard)
|
||||
samples_to_get = data->samples_to_discard;
|
||||
data->samples_to_discard -= samples_to_get;
|
||||
}
|
||||
else {
|
||||
/* get max samples and copy */
|
||||
if (samples_to_get > samples_to_do - samples_done)
|
||||
samples_to_get = samples_to_do - samples_done;
|
||||
|
||||
memcpy(outbuf + samples_done*channels,
|
||||
data->sample_buffer + data->samples_used*channels,
|
||||
samples_to_get*channels * sizeof(sample));
|
||||
|
||||
samples_done += samples_to_get;
|
||||
}
|
||||
|
||||
/* mark consumed samples */
|
||||
data->samples_used += samples_to_get;
|
||||
data->samples_filled -= samples_to_get;
|
||||
}
|
||||
else { /* decode data */
|
||||
int iframe, status;
|
||||
int bytes_used = 0;
|
||||
uint8_t *buffer = data->data_buffer;
|
||||
size_t bytes;
|
||||
Atrac9CodecInfo info = {0};
|
||||
|
||||
data->samples_used = 0;
|
||||
|
||||
/* ATRAC9 is made of decodable superframes with several sub-frames. AT9 config data gives
|
||||
* superframe size, number of frames and samples (~100-200 bytes and ~256/1024 samples). */
|
||||
status = Atrac9GetCodecInfo(data->handle, &info);
|
||||
if (status < 0) goto decode_fail;
|
||||
|
||||
|
||||
/* preadjust */ //todo improve
|
||||
switch(data->config.type) {
|
||||
case ATRAC9_XVAG:
|
||||
/* PS4 (ex. The Last of Us) has a RIFF AT9 (can be ignored) instead of the first superframe.
|
||||
* As subsongs do too, needs to be skipped here instead of adjusting start_offset */
|
||||
if (stream->offset == stream->channel_start_offset) {
|
||||
if (read_32bitBE(stream->offset, stream->streamfile) == 0x00000000 /* padding before RIFF */
|
||||
&& read_32bitBE(stream->offset + info.superframeSize - 0x08,stream->streamfile) == 0x64617461) { /* RIFF's "data" */
|
||||
stream->offset += info.superframeSize;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* read one raw block (superframe) and advance offsets */
|
||||
bytes = read_streamfile(data->data_buffer,stream->offset, info.superframeSize,stream->streamfile);
|
||||
if (bytes != data->data_buffer_size) {
|
||||
VGM_LOG("ATRAC9: read %x vs expected %x bytes at %lx\n", bytes, info.superframeSize, stream->offset);
|
||||
goto decode_fail;
|
||||
}
|
||||
|
||||
stream->offset += bytes;
|
||||
|
||||
/* postadjust */ //todo improve
|
||||
switch(data->config.type) {
|
||||
case ATRAC9_XVAG: /* skip other subsong blocks in XVAG */
|
||||
if (data->config.interleave_skip && ((stream->offset - stream->channel_start_offset) % data->config.interleave_skip == 0)) {
|
||||
stream->offset += data->config.interleave_skip * (data->config.subsong_skip - 1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* decode all frames in the superframe block */
|
||||
for (iframe = 0; iframe < info.framesInSuperframe; iframe++) {
|
||||
status = Atrac9Decode(data->handle, buffer, data->sample_buffer + data->samples_filled*channels, &bytes_used);
|
||||
if (status < 0) goto decode_fail;
|
||||
|
||||
buffer += bytes_used;
|
||||
data->samples_filled += info.frameSamples;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
decode_fail:
|
||||
/* on error just put some 0 samples */
|
||||
VGM_LOG("ATRAC9: decode fail at %lx, missing %i samples\n", stream->offset, (samples_to_do - samples_done));
|
||||
memset(outbuf + samples_done * channels, 0, (samples_to_do - samples_done) * sizeof(sample) * channels);
|
||||
}
|
||||
|
||||
void reset_atrac9(VGMSTREAM *vgmstream) {
|
||||
atrac9_codec_data *data = vgmstream->codec_data;
|
||||
|
||||
if (!data->handle)
|
||||
goto fail;
|
||||
|
||||
#if 0
|
||||
/* reopen/flush, not needed as superframes decode separatedly and there is no carried state */
|
||||
{
|
||||
int status;
|
||||
uint8_t config_data[4];
|
||||
|
||||
Atrac9ReleaseHandle(data->handle);
|
||||
data->handle = Atrac9GetHandle();
|
||||
if (!data->handle) goto fail;
|
||||
|
||||
put_32bitBE(config_data, data->config.config_data);
|
||||
status = Atrac9InitDecoder(data->handle, config_data);
|
||||
if (status < 0) goto fail;
|
||||
}
|
||||
#endif
|
||||
|
||||
data->samples_used = 0;
|
||||
data->samples_filled = 0;
|
||||
data->samples_to_discard = 0;
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
return; /* decode calls should fail... */
|
||||
}
|
||||
|
||||
void seek_atrac9(VGMSTREAM *vgmstream, int32_t num_sample) {
|
||||
atrac9_codec_data *data = vgmstream->codec_data;
|
||||
|
||||
reset_atrac9(vgmstream);
|
||||
|
||||
data->samples_to_discard = num_sample;
|
||||
data->samples_to_discard += data->config.encoder_delay;
|
||||
|
||||
/* loop offsets are set during decode; force them to stream start so discard works */
|
||||
if (vgmstream->loop_ch)
|
||||
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset;
|
||||
}
|
||||
|
||||
void free_atrac9(atrac9_codec_data *data) {
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
if (data->handle) Atrac9ReleaseHandle(data->handle);
|
||||
free(data->data_buffer);
|
||||
free(data->sample_buffer);
|
||||
free(data);
|
||||
}
|
||||
|
||||
|
||||
size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data *data) {
|
||||
Atrac9CodecInfo info = {0};
|
||||
int status;
|
||||
|
||||
status = Atrac9GetCodecInfo(data->handle, &info);
|
||||
if (status < 0) goto fail;
|
||||
|
||||
return bytes / info.superframeSize * (info.frameSamples * info.framesInSuperframe);
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
#endif
|
|
@ -65,6 +65,7 @@ void decode_pcm8_sb_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channels
|
|||
void decode_pcm8_unsigned_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
void decode_pcm8_unsigned(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
void decode_ulaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
void decode_ulaw_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
void decode_alaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
|
||||
void decode_pcmfloat(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int big_endian);
|
||||
size_t pcm_bytes_to_samples(size_t bytes, int channels, int bits_per_sample);
|
||||
|
@ -79,7 +80,6 @@ size_t ps_bytes_to_samples(size_t bytes, int channels);
|
|||
|
||||
/* xa_decoder */
|
||||
void decode_xa(VGMSTREAM * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
|
||||
void xa_init_get_high_nibble(VGMSTREAM * vgmstream);
|
||||
size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked);
|
||||
|
||||
/* ea_xa_decoder */
|
||||
|
@ -157,7 +157,7 @@ void seek_ogg_vorbis(VGMSTREAM *vgmstream, int32_t num_sample);
|
|||
void free_ogg_vorbis(ogg_vorbis_codec_data *data);
|
||||
|
||||
/* vorbis_custom_decoder */
|
||||
vorbis_custom_codec_data * init_vorbis_custom_codec_data(STREAMFILE *streamfile, off_t start_offset, vorbis_custom_t type, vorbis_custom_config * config);
|
||||
vorbis_custom_codec_data *init_vorbis_custom(STREAMFILE *streamfile, off_t start_offset, vorbis_custom_t type, vorbis_custom_config * config);
|
||||
void decode_vorbis_custom(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels);
|
||||
void reset_vorbis_custom(VGMSTREAM *vgmstream);
|
||||
void seek_vorbis_custom(VGMSTREAM *vgmstream, int32_t num_sample);
|
||||
|
@ -166,11 +166,9 @@ void free_vorbis_custom(vorbis_custom_codec_data *data);
|
|||
|
||||
#ifdef VGM_USE_MPEG
|
||||
/* mpeg_decoder */
|
||||
mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels);
|
||||
mpeg_codec_data *init_mpeg_custom_codec_data(STREAMFILE *streamFile, off_t start_offset, coding_t *coding_type, int channels, mpeg_custom_t custom_type, mpeg_custom_config *config);
|
||||
|
||||
mpeg_codec_data *init_mpeg(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels);
|
||||
mpeg_codec_data *init_mpeg_custom(STREAMFILE *streamFile, off_t start_offset, coding_t *coding_type, int channels, mpeg_custom_t custom_type, mpeg_custom_config *config);
|
||||
void decode_mpeg(VGMSTREAM * vgmstream, sample * outbuf, int32_t samples_to_do, int channels);
|
||||
void decode_fake_mpeg2_l2(VGMSTREAMCHANNEL * stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do);
|
||||
void reset_mpeg(VGMSTREAM *vgmstream);
|
||||
void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample);
|
||||
void free_mpeg(mpeg_codec_data *data);
|
||||
|
@ -204,18 +202,29 @@ void free_mp4_aac(mp4_aac_codec_data * data);
|
|||
#endif
|
||||
|
||||
#ifdef VGM_USE_MAIATRAC3PLUS
|
||||
/* at3_decoder */
|
||||
/* at3plus_decoder */
|
||||
maiatrac3plus_codec_data *init_at3plus();
|
||||
void decode_at3plus(VGMSTREAM *vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel);
|
||||
void reset_at3plus(VGMSTREAM *vgmstream);
|
||||
void seek_at3plus(VGMSTREAM *vgmstream, int32_t num_sample);
|
||||
void free_at3plus(maiatrac3plus_codec_data *data);
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
/* atrac9_decoder */
|
||||
atrac9_codec_data *init_atrac9(atrac9_config *cfg);
|
||||
void decode_atrac9(VGMSTREAM *vgmstream, sample * outbuf, int32_t samples_to_do, int channels);
|
||||
void reset_atrac9(VGMSTREAM *vgmstream);
|
||||
void seek_atrac9(VGMSTREAM *vgmstream, int32_t num_sample);
|
||||
void free_atrac9(atrac9_codec_data *data);
|
||||
size_t atrac9_bytes_to_samples(size_t bytes, atrac9_codec_data *data);
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
/* ffmpeg_decoder */
|
||||
ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size);
|
||||
ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size);
|
||||
ffmpeg_codec_data * init_ffmpeg_config(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size, ffmpeg_custom_config * config);
|
||||
ffmpeg_codec_data *init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size);
|
||||
ffmpeg_codec_data *init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size);
|
||||
ffmpeg_codec_data *init_ffmpeg_config(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size, ffmpeg_custom_config * config);
|
||||
|
||||
void decode_ffmpeg(VGMSTREAM *stream, sample * outbuf, int32_t samples_to_do, int channels);
|
||||
void reset_ffmpeg(VGMSTREAM *vgmstream);
|
||||
|
|
|
@ -119,8 +119,8 @@ int ffmpeg_custom_read_eaxma(ffmpeg_codec_data *data, uint8_t *buf, int buf_size
|
|||
virtual_base += data_size;
|
||||
}
|
||||
|
||||
/* exit on last block just in case, though should reach file size */
|
||||
if (block_size & 0x80000000)
|
||||
/* exit on last block just in case, though should reach real_size */
|
||||
if ((block_size & 0x80000000) || (block_size & 0x45000000))
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -199,10 +199,9 @@ size_t ffmpeg_get_eaxma_virtual_size(int channels, off_t real_offset, size_t rea
|
|||
/* 0x04(4): decoded samples */
|
||||
off_t packets_offset = real_offset + 0x08;
|
||||
|
||||
if ((block_size & 0xFF000000) && !(block_size & 0x80000000)) {
|
||||
VGM_LOG("EA-XMA: unknown flag found at %lx\n", (off_t)real_offset);
|
||||
goto fail;
|
||||
}
|
||||
/* At 0x00(1): block flag
|
||||
* - in SNS: 0x00=normal block, 0x80=last block (not mandatory)
|
||||
* - in SPS: 0x48=header, 0x44=normal block, 0x45=last block (empty) */
|
||||
|
||||
max_packets = get_block_max_packets(num_streams, packets_offset, streamFile);
|
||||
if (max_packets == 0) goto fail;
|
||||
|
@ -213,7 +212,7 @@ size_t ffmpeg_get_eaxma_virtual_size(int channels, off_t real_offset, size_t rea
|
|||
real_offset += (block_size & 0x00FFFFFF);
|
||||
|
||||
/* exit on last block just in case, though should reach real_size */
|
||||
if (block_size & 0x80000000)
|
||||
if ((block_size & 0x80000000) || (block_size & 0x45000000))
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -787,12 +787,13 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci
|
|||
|
||||
size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels) {
|
||||
/* MS IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */
|
||||
return (bytes / block_align) * (block_align - 4 * channels) * 2 / channels;
|
||||
return (bytes / block_align) * (block_align - 4 * channels) * 2 / channels
|
||||
+ ((bytes % block_align) ? ((bytes % block_align) - 4 * channels) * 2 / channels : 0);
|
||||
}
|
||||
|
||||
size_t ima_bytes_to_samples(size_t bytes, int channels) {
|
||||
/* 2 samples per byte (2 nibbles) in stereo or mono config */
|
||||
return bytes / channels * 2;
|
||||
return bytes * 2 / channels;
|
||||
}
|
||||
|
||||
size_t ubi_ima_bytes_to_samples(size_t bytes, int channels, STREAMFILE *streamFile, off_t offset) {
|
||||
|
|
|
@ -21,7 +21,7 @@ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data
|
|||
|
||||
|
||||
/* Inits regular MPEG */
|
||||
mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels) {
|
||||
mpeg_codec_data *init_mpeg(STREAMFILE *streamfile, off_t start_offset, coding_t *coding_type, int channels) {
|
||||
mpeg_codec_data *data = NULL;
|
||||
|
||||
/* init codec */
|
||||
|
@ -116,7 +116,7 @@ fail:
|
|||
|
||||
|
||||
/* Init custom MPEG, with given type and config */
|
||||
mpeg_codec_data *init_mpeg_custom_codec_data(STREAMFILE *streamFile, off_t start_offset, coding_t *coding_type, int channels, mpeg_custom_t type, mpeg_custom_config *config) {
|
||||
mpeg_codec_data *init_mpeg_custom(STREAMFILE *streamFile, off_t start_offset, coding_t *coding_type, int channels, mpeg_custom_t type, mpeg_custom_config *config) {
|
||||
mpeg_codec_data *data = NULL;
|
||||
int i, ok;
|
||||
|
||||
|
|
|
@ -87,69 +87,83 @@ void decode_pcm16LE_XOR_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int chan
|
|||
}
|
||||
}
|
||||
|
||||
/* decodes u-law (ITU G.711 non-linear PCM), from g711.c */
|
||||
void decode_ulaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
int i;
|
||||
int32_t sample_count;
|
||||
int sign, segment, quantization, sample;
|
||||
static int expand_ulaw(uint8_t ulawbyte) {
|
||||
int sign, segment, quantization, new_sample;
|
||||
const int bias = 0x84;
|
||||
|
||||
ulawbyte = ~ulawbyte; /* stored in complement */
|
||||
sign = (ulawbyte & 0x80);
|
||||
segment = (ulawbyte & 0x70) >> 4; /* exponent */
|
||||
quantization = ulawbyte & 0x0F; /* mantissa */
|
||||
|
||||
new_sample = (quantization << 3) + bias; /* add bias */
|
||||
new_sample <<= segment;
|
||||
new_sample = (sign) ? (bias - new_sample) : (new_sample - bias); /* remove bias */
|
||||
|
||||
#if 0 // the above follows Sun's implementation, but this works too
|
||||
{
|
||||
static int exp_lut[8] = {0,132,396,924,1980,4092,8316,16764}; /* precalcs from bias */
|
||||
new_sample = exp_lut[segment] + (quantization << (segment + 3));
|
||||
if (sign != 0) new_sample = -new_sample;
|
||||
}
|
||||
#endif
|
||||
|
||||
return new_sample;
|
||||
}
|
||||
|
||||
/* decodes u-law (ITU G.711 non-linear PCM), from g711.c */
|
||||
void decode_ulaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
int i, sample_count;
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
uint8_t ulawbyte = read_8bit(stream->offset+i,stream->streamfile);
|
||||
|
||||
ulawbyte = ~ulawbyte; /* stored in complement */
|
||||
sign = (ulawbyte & 0x80);
|
||||
segment = (ulawbyte & 0x70) >> 4; /* exponent */
|
||||
quantization = ulawbyte & 0x0F; /* mantissa */
|
||||
|
||||
sample = (quantization << 3) + bias; /* add bias */
|
||||
sample <<= segment;
|
||||
sample = (sign) ? (bias - sample) : (sample - bias); /* remove bias */
|
||||
|
||||
#if 0 // the above follows Sun's implementation, but this works too
|
||||
{
|
||||
static int exp_lut[8] = {0,132,396,924,1980,4092,8316,16764}; /* precalcs from bias */
|
||||
sample = exp_lut[segment] + (quantization << (segment + 3));
|
||||
if (sign != 0) sample = -sample;
|
||||
}
|
||||
#endif
|
||||
|
||||
outbuf[sample_count] = sample;
|
||||
outbuf[sample_count] = expand_ulaw(ulawbyte);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void decode_ulaw_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
int i, sample_count;
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
uint8_t ulawbyte = read_8bit(stream->offset+i*channelspacing,stream->streamfile);
|
||||
outbuf[sample_count] = expand_ulaw(ulawbyte);
|
||||
}
|
||||
}
|
||||
|
||||
static int expand_alaw(uint8_t alawbyte) {
|
||||
int sign, segment, quantization, new_sample;
|
||||
|
||||
alawbyte ^= 0x55;
|
||||
sign = (alawbyte & 0x80);
|
||||
segment = (alawbyte & 0x70) >> 4; /* exponent */
|
||||
quantization = alawbyte & 0x0F; /* mantissa */
|
||||
|
||||
new_sample = (quantization << 4);
|
||||
switch (segment) {
|
||||
case 0:
|
||||
new_sample += 8;
|
||||
break;
|
||||
case 1:
|
||||
new_sample += 0x108;
|
||||
break;
|
||||
default:
|
||||
new_sample += 0x108;
|
||||
new_sample <<= segment - 1;
|
||||
break;
|
||||
}
|
||||
new_sample = (sign) ? new_sample : -new_sample;
|
||||
|
||||
return new_sample;
|
||||
}
|
||||
|
||||
/* decodes a-law (ITU G.711 non-linear PCM), from g711.c */
|
||||
void decode_alaw(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
int i;
|
||||
int32_t sample_count;
|
||||
int sign, segment, quantization, sample;
|
||||
|
||||
int i, sample_count;
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
uint8_t alawbyte = read_8bit(stream->offset+i,stream->streamfile);
|
||||
|
||||
alawbyte ^= 0x55;
|
||||
sign = (alawbyte & 0x80);
|
||||
segment = (alawbyte & 0x70) >> 4; /* exponent */
|
||||
quantization = alawbyte & 0x0F; /* mantissa */
|
||||
|
||||
sample = (quantization << 4);
|
||||
switch (segment) {
|
||||
case 0:
|
||||
sample += 8;
|
||||
break;
|
||||
case 1:
|
||||
sample += 0x108;
|
||||
break;
|
||||
default:
|
||||
sample += 0x108;
|
||||
sample <<= segment - 1;
|
||||
break;
|
||||
}
|
||||
sample = (sign) ? sample : -sample;
|
||||
|
||||
outbuf[sample_count] = sample;
|
||||
outbuf[sample_count] = expand_alaw(alawbyte);;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,12 +6,25 @@
|
|||
#define VAG_USE_INTEGER_TABLE 0
|
||||
|
||||
/* PS ADPCM table (precalculated divs) */
|
||||
static const double VAG_f[5][2] = {
|
||||
static const double VAG_f[16][2] = {
|
||||
{ 0.0 , 0.0 },
|
||||
{ 60.0 / 64.0 , 0.0 },
|
||||
{ 115.0 / 64.0 , -52.0 / 64.0 },
|
||||
{ 98.0 / 64.0 , -55.0 / 64.0 },
|
||||
{ 122.0 / 64.0 , -60.0 / 64.0 }
|
||||
{ 122.0 / 64.0 , -60.0 / 64.0 },
|
||||
/* extended table from PPSSPP (PSP emu), found by tests
|
||||
* (only seen in inFamous PS3, very rare, possibly "SVAG" or "VAG-HE") */
|
||||
{ 0.0 , 0.0 },
|
||||
{ 0.0 , 0.0 },
|
||||
{ 52.0 / 64.0 , 0.0 },
|
||||
{ 55.0 / 64.0 , -2.0 / 64.0 },
|
||||
{ 60.0 / 64.0 ,-125.0 / 64.0 },
|
||||
{ 0.0 , 0.0 },
|
||||
{ 0.0 , -91.0 / 64.0 },
|
||||
{ 0.0 , 0.0 },
|
||||
{ 2.0 / 64.0 ,-216.0 / 64.0 },
|
||||
{ 125.0 / 64.0 , -6.0 / 64.0 },
|
||||
{ 0.0 ,-151.0 / 64.0 },
|
||||
};
|
||||
#if VAG_USE_INTEGER_TABLE
|
||||
/* PS ADPCM table */
|
||||
|
@ -20,7 +33,19 @@ static const int8_t VAG_coefs[5][2] = {
|
|||
{ 60 , 0 },
|
||||
{ 115 , -52 },
|
||||
{ 98 , -55 },
|
||||
{ 122 , -60 }
|
||||
{ 122 , -60 },
|
||||
/* extended */
|
||||
{ 0 , 0 },
|
||||
{ 0 , 0 },
|
||||
{ 52 , 0 },
|
||||
{ 55 , -2 },
|
||||
{ 60 ,-125 },
|
||||
{ 0 , 0 },
|
||||
{ 0 , -91 },
|
||||
{ 0 , 0 },
|
||||
{ 2 ,-216 },
|
||||
{ 125 , -6 },
|
||||
{ 0 ,-151 },
|
||||
};
|
||||
#endif
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ static void pcm_convert_float_to_16(vorbis_custom_codec_data * data, sample * ou
|
|||
*
|
||||
* Reference: https://www.xiph.org/vorbis/doc/libvorbis/overview.html
|
||||
*/
|
||||
vorbis_custom_codec_data * init_vorbis_custom_codec_data(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_t type, vorbis_custom_config * config) {
|
||||
vorbis_custom_codec_data * init_vorbis_custom(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_t type, vorbis_custom_config * config) {
|
||||
vorbis_custom_codec_data * data = NULL;
|
||||
int ok;
|
||||
|
||||
|
|
|
@ -20,10 +20,6 @@ static int CLAMP(int value, int Minim, int Maxim)
|
|||
return value;
|
||||
}
|
||||
|
||||
void xa_init_get_high_nibble(VGMSTREAM *vgmstream) {
|
||||
vgmstream->xa_get_high_nibble=1;
|
||||
}
|
||||
|
||||
void decode_xa(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
|
||||
VGMSTREAMCHANNEL * stream = &(vgmstream->ch[channel]);
|
||||
|
|
|
@ -42,6 +42,7 @@ static const char* extension_list[] = {
|
|||
"ass",
|
||||
"ast",
|
||||
"at3",
|
||||
"at9",
|
||||
"aud",
|
||||
"aus",
|
||||
"awc",
|
||||
|
@ -106,7 +107,7 @@ static const char* extension_list[] = {
|
|||
"emff",
|
||||
"enth",
|
||||
"exa",
|
||||
"ezw",
|
||||
"ezw",
|
||||
|
||||
"fag",
|
||||
"ffw",
|
||||
|
@ -156,6 +157,8 @@ static const char* extension_list[] = {
|
|||
"khv",
|
||||
"kovs",
|
||||
"kraw",
|
||||
"ktss",
|
||||
"kvs",
|
||||
|
||||
"laac", //fake extension, for AAC (tri-Ace/FFmpeg)
|
||||
"lac3", //fake extension, for AC3
|
||||
|
@ -181,7 +184,7 @@ static const char* extension_list[] = {
|
|||
"mic",
|
||||
"mihb",
|
||||
"mnstr",
|
||||
"mogg",
|
||||
"mogg",
|
||||
//"mp4", //common
|
||||
//"mpc", //FFmpeg, not parsed (musepack) //common
|
||||
"mpdsp",
|
||||
|
@ -423,6 +426,7 @@ static const coding_info coding_info_list[] = {
|
|||
{coding_PCM8_U_int, "8-bit unsigned PCM with 1 byte interleave (block)"},
|
||||
{coding_PCM8_SB_int, "8-bit PCM with sign bit, 1 byte interleave (block)"},
|
||||
{coding_ULAW, "8-bit u-Law"},
|
||||
{coding_ULAW_int, "8-bit u-Law with 1 byte interleave (block)"},
|
||||
{coding_ALAW, "8-bit a-Law"},
|
||||
{coding_PCMFLOAT, "32-bit float PCM"},
|
||||
|
||||
|
@ -521,6 +525,9 @@ static const coding_info coding_info_list[] = {
|
|||
#ifdef VGM_USE_MAIATRAC3PLUS
|
||||
{coding_AT3plus, "ATRAC3plus"},
|
||||
#endif
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
{coding_ATRAC9, "ATRAC9"},
|
||||
#endif
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{coding_FFmpeg, "FFmpeg"},
|
||||
#endif
|
||||
|
@ -879,6 +886,7 @@ static const meta_info meta_info_list[] = {
|
|||
{meta_CSTM, "Nintendo 3DS CSTM Header"},
|
||||
{meta_FSTM, "Nintendo Wii U FSTM Header"},
|
||||
{meta_KT_WIIBGM, "Koei Tecmo WiiBGM Header"},
|
||||
{meta_KTSS, "Koei Tecmo Switch Sound Header"},
|
||||
{meta_3DS_IDSP, "Nintendo IDSP Header"},
|
||||
{meta_WIIU_BTSND, "Nintendo Wii U Menu Boot Sound"},
|
||||
{meta_MCA, "Capcom MCA header"},
|
||||
|
@ -900,6 +908,7 @@ static const meta_info meta_info_list[] = {
|
|||
{meta_GTD, "GTD/GHS header"},
|
||||
{meta_TA_AAC_X360, "tri-Ace AAC (X360) header"},
|
||||
{meta_TA_AAC_PS3, "tri-Ace AAC (PS3) header"},
|
||||
{meta_TA_AAC_VORBIS, "tri-Ace AAC (Mobile Vorbis) header"},
|
||||
{meta_PS3_MTA2, "Konami MTA2 header"},
|
||||
{meta_NGC_ULW, "Criterion ULW raw header"},
|
||||
{meta_PC_XA30, "Reflections XA30 PC header"},
|
||||
|
@ -923,8 +932,7 @@ static const meta_info meta_info_list[] = {
|
|||
{meta_EA_SPS, "Electronic Arts SPS header"},
|
||||
{meta_NGC_VID1, "Neversoft VID1 header"},
|
||||
{meta_PC_FLX, "Ultima IX .FLX header"},
|
||||
{meta_MOGG, "Harmonix Music Systems MOGG Vorbis "},
|
||||
|
||||
{meta_MOGG, "Harmonix Music Systems MOGG Vorbis"},
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
{meta_OGG_VORBIS, "Ogg Vorbis"},
|
||||
|
|
|
@ -4,36 +4,41 @@
|
|||
|
||||
/* set up for the block at the given offset */
|
||||
void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
int i;
|
||||
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
|
||||
uint32_t id;
|
||||
size_t file_size, block_size = 0, block_header = 0;
|
||||
int i;
|
||||
size_t block_size = 0, block_header = 0;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE;
|
||||
size_t file_size = get_streamfile_size(streamFile);
|
||||
|
||||
|
||||
/* find target block ID and skip the rest */
|
||||
file_size = get_streamfile_size(streamFile);
|
||||
while (block_offset < file_size) {
|
||||
id = read_32bitBE(block_offset+0x00,streamFile);
|
||||
block_size = read_32bit(block_offset+0x04,streamFile); /* includes id/size */
|
||||
block_header = 0x0;
|
||||
uint32_t id = read_32bitBE(block_offset+0x00,streamFile);
|
||||
|
||||
if (id == 0x31534E68) { /* "1SNh" header block found */
|
||||
block_header = read_32bitBE(block_offset+0x08, streamFile) == 0x45414353 ? 0x28 : 0x2c; /* "EACS" */
|
||||
if (block_header < block_size) /* sometimes has data */
|
||||
break;
|
||||
block_size = read_32bitLE(block_offset+0x04,streamFile);
|
||||
if (block_size > 0x00F00000) /* BE in SAT, but one file may have both BE and LE chunks */
|
||||
block_size = read_32bitBE(block_offset+0x04,streamFile);
|
||||
|
||||
block_header = 0;
|
||||
|
||||
if (id == 0x31534E68 || id == 0x53454144) { /* "1SNh" "SEAD" audio header */
|
||||
int is_sead = (id == 0x53454144);
|
||||
int is_eacs = read_32bitBE(block_offset+0x08, streamFile) == 0x45414353;
|
||||
|
||||
block_header = is_eacs ? 0x28 : (is_sead ? 0x14 : 0x2c);
|
||||
if (block_header >= block_size) /* sometimes has audio data after header */
|
||||
block_header = 0;
|
||||
}
|
||||
|
||||
if (id == 0x31534E64) { /* "1SNd" data block found */
|
||||
else if (id == 0x31534E64 || id == 0x534E4443) { /* "1SNd" "SNDC" audio data */
|
||||
block_header = 0x08;
|
||||
}
|
||||
else if (id == 0x00000000) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (id == 0x00000000 || id == 0xFFFFFFFF) { /* EOF: possible? */
|
||||
if (block_header) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* any other blocks "1SNl" "1SNe" etc */ //todo parse movie blocks
|
||||
block_offset += block_size;
|
||||
}
|
||||
|
||||
|
@ -45,6 +50,7 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream) {
|
|||
/* set new channel offsets and block sizes */
|
||||
switch(vgmstream->coding_type) {
|
||||
case coding_PCM8_int:
|
||||
case coding_ULAW_int:
|
||||
vgmstream->current_block_size /= vgmstream->channels;
|
||||
for (i=0;i<vgmstream->channels;i++) {
|
||||
vgmstream->ch[i].offset = block_offset + block_header + i;
|
||||
|
@ -66,13 +72,24 @@ void block_update_ea_1snh(off_t block_offset, VGMSTREAM * vgmstream) {
|
|||
break;
|
||||
|
||||
case coding_DVI_IMA:
|
||||
vgmstream->current_block_size -= 0x14;
|
||||
for(i = 0; i < vgmstream->channels; i++) {
|
||||
off_t adpcm_offset = block_offset + block_header + 0x04;
|
||||
vgmstream->ch[i].adpcm_step_index = read_32bit(adpcm_offset + i*0x04, streamFile);
|
||||
vgmstream->ch[i].adpcm_history1_32 = read_32bit(adpcm_offset + 0x04*vgmstream->channels + i*0x04, streamFile);
|
||||
// todo some demuxed vids don't have ADPCM hist? not sure how to correctly detect
|
||||
vgmstream->ch[i].offset = block_offset + block_header + 0x14;
|
||||
if (vgmstream->codec_version == 1) { /* ADPCM hist */
|
||||
vgmstream->current_block_samples = read_32bit(block_offset + block_header, streamFile);
|
||||
vgmstream->current_block_size = 0; // - (0x04 + 0x08*vgmstream->channels); /* should be equivalent */
|
||||
|
||||
for(i = 0; i < vgmstream->channels; i++) {
|
||||
off_t adpcm_offset = block_offset + block_header + 0x04;
|
||||
vgmstream->ch[i].adpcm_step_index = read_32bit(adpcm_offset + i*0x04 + 0x00*vgmstream->channels, streamFile);
|
||||
vgmstream->ch[i].adpcm_history1_32 = read_32bit(adpcm_offset + i*0x04 + 0x04*vgmstream->channels, streamFile);
|
||||
vgmstream->ch[i].offset = adpcm_offset + 0x08*vgmstream->channels;
|
||||
}
|
||||
|
||||
VGM_ASSERT(vgmstream->current_block_samples != (block_size - block_header - 0x04 - 0x08*vgmstream->channels) * 2 / vgmstream->channels,
|
||||
"EA 1SHN blocked: different expected vs block num samples at %lx\n", block_offset);
|
||||
}
|
||||
else {
|
||||
for(i = 0; i < vgmstream->channels; i++) {
|
||||
vgmstream->ch[i].offset = block_offset + block_header;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -4,66 +4,58 @@
|
|||
|
||||
/* set up for the block at the given offset */
|
||||
void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
|
||||
int i;
|
||||
int new_schl = 0;
|
||||
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
|
||||
uint32_t id;
|
||||
size_t file_size, block_size = 0, block_samples;
|
||||
size_t block_size = 0, block_samples = 0;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE;
|
||||
//int16_t (*read_16bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_16bitBE : read_16bitLE;
|
||||
size_t file_size = get_streamfile_size(streamFile);
|
||||
|
||||
|
||||
/* find target block ID and skip the rest */
|
||||
file_size = get_streamfile_size(streamFile);
|
||||
while (block_offset < file_size) {
|
||||
id = read_32bitBE(block_offset+0x00,streamFile);
|
||||
uint32_t id = read_32bitBE(block_offset+0x00,streamFile);
|
||||
|
||||
block_size = read_32bitLE(block_offset+0x04,streamFile);
|
||||
if (block_size > 0x00F00000) /* size is always LE, except in early SS/MAC */
|
||||
if (block_size > 0x00F00000) /* size is always LE, except in early SAT/MAC */
|
||||
block_size = read_32bitBE(block_offset+0x04,streamFile);
|
||||
|
||||
/* SCxx blocks have size in the header, but others may not. To simplify we just try to find
|
||||
* a SCDl (main data) every 0x04. EA sometimes concats many small files, so after a SCEl (end block)
|
||||
* there may be a new SCHl + SCDl too, so this pretends they are a single stream. */
|
||||
if (id == 0x5343446C) { /* "SCDl" data block found */
|
||||
block_samples = 0;
|
||||
|
||||
/* use num_samples from header if possible; don't calc as data may have padding (ex. PCM8) or not possible (ex. MP3) */
|
||||
if (id == 0x5343446C || id == 0x5344454E) { /* "SCDl" "SDEN" audio data */
|
||||
switch(vgmstream->coding_type) {
|
||||
case coding_PSX:
|
||||
block_samples = ps_bytes_to_samples(block_size-0x10, vgmstream->channels);
|
||||
break;
|
||||
|
||||
default:
|
||||
block_samples = read_32bit(block_offset+0x08,streamFile);
|
||||
break;
|
||||
}
|
||||
|
||||
/* guard against false positives (happens in "pIQT" blocks) */
|
||||
if (block_size > 0xFFFF || block_samples > 0xFFFF) { /* observed max is ~0xf00 but who knows */
|
||||
block_offset += 0x04;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
else {
|
||||
/* movie "pIQT" may be bigger than what block_size says, but seems to help */
|
||||
if (id == 0x5343486C || id == 0x5343436C || id == 0x53434C6C || id == 0x70495154) { /* "SCHl" "SCCl" "SCLl" "SCEl" "pIQT" */
|
||||
block_offset += block_size;
|
||||
} else {
|
||||
block_offset += 0x04;
|
||||
else { /* any other chunk, audio ("SCHl" "SCCl" "SCLl" "SCEl" etc), or video ("pQGT" "pIQT "MADk" etc) */
|
||||
/* padding between "SCEl" and next "SCHl" (when subfiles exist) */
|
||||
if (id == 0x00000000) {
|
||||
block_size = 0x04;
|
||||
}
|
||||
|
||||
if (id == 0x5343456C) { /* "SCEl" end block found */
|
||||
block_offset += (block_offset % 0x04) == 0 ? 0 : 0x04 - (block_offset % 0x04); /* 32b-aligned, important */
|
||||
/* Usually there is padding between SCEl and SCHl too (aligned to 0x80) */
|
||||
}
|
||||
|
||||
if (id == 0x5343486C) { /* "SCHl", new subfile */
|
||||
if (id == 0x5343486C || id == 0x5348454E) { /* "SCHl" "SHEN" end block */
|
||||
new_schl = 1;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
/* guard against errors (happens in bad rips/endianness, observed max is vid ~0x20000) */
|
||||
if (block_size == 0x00 || block_size > 0xFFFFF || block_samples > 0xFFFF) {
|
||||
block_size = 0x04;
|
||||
block_samples = 0;
|
||||
}
|
||||
|
||||
|
||||
if (block_samples) /* audio found */
|
||||
break;
|
||||
block_offset += block_size;
|
||||
|
||||
/* "SCEl" are aligned to 0x80 usually, but causes problems if not 32b-aligned (ex. Need for Speed 2 PC) */
|
||||
if ((id == 0x5343456C || id == 0x5345454E) && block_offset % 0x04) {
|
||||
block_offset += 0x04 - (block_offset % 0x04);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,13 +8,13 @@ void xa_block_update(off_t block_offset, VGMSTREAM * vgmstream) {
|
|||
int8_t currentChannel=0;
|
||||
int8_t subAudio=0;
|
||||
|
||||
xa_init_get_high_nibble(vgmstream);
|
||||
vgmstream->xa_get_high_nibble = 1; /* reset nibble order */
|
||||
|
||||
/* don't change this variable in the init process */
|
||||
if (vgmstream->samples_into_block != 0)
|
||||
vgmstream->xa_sector_length += 0x80;
|
||||
|
||||
/* XA mode2/form2 sector
|
||||
/* XA mode2/form2 sector, size 0x930
|
||||
* 0x00: sync word
|
||||
* 0x0c: header = minute, second, sector, mode (always 0x02)
|
||||
* 0x10: subheader = file, channel (marker), submode flags, xa header
|
||||
|
@ -23,9 +23,10 @@ void xa_block_update(off_t block_offset, VGMSTREAM * vgmstream) {
|
|||
* 0x918: unused
|
||||
* 0x92c: EDC/checksum or null
|
||||
* 0x930: end
|
||||
* (in non-blocked ISO 2048 mode1/data chunks are 0x800)
|
||||
*/
|
||||
|
||||
/* submode flags (typical audio value = 0x64)
|
||||
/* submode flag bits (typical audio value = 0x64)
|
||||
* - 7: end of file
|
||||
* - 6: real time mode
|
||||
* - 5: sector form (0=form1, 1=form2)
|
||||
|
|
|
@ -64,7 +64,7 @@ VGMSTREAM * init_vgmstream_ahx(STREAMFILE *streamFile) {
|
|||
}
|
||||
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, channel_count, MPEG_AHX, &cfg);
|
||||
vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, channel_count, MPEG_AHX, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
#else
|
||||
goto fail;
|
||||
|
|
|
@ -74,7 +74,7 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) {
|
|||
cfg.chunk_size = awc.block_chunk;
|
||||
cfg.big_endian = awc.big_endian;
|
||||
|
||||
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, awc.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_AWC, &cfg);
|
||||
vgmstream->codec_data = init_mpeg_custom(streamFile, awc.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_AWC, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include "../layout/layout.h"
|
||||
|
||||
#define EA_CODEC_PCM 0x00
|
||||
//#define EA_CODEC_??? 0x01 //used in SAT videos
|
||||
#define EA_CODEC_ULAW 0x01
|
||||
#define EA_CODEC_IMA 0x02
|
||||
#define EA_CODEC_PSX 0xFF //fake value
|
||||
|
||||
|
@ -20,10 +20,13 @@ typedef struct {
|
|||
|
||||
int big_endian;
|
||||
int loop_flag;
|
||||
int is_sead;
|
||||
int codec_version;
|
||||
} ea_header;
|
||||
|
||||
static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset);
|
||||
static void set_ea_1snh_psx_samples(STREAMFILE* streamFile, off_t start_offset, ea_header* ea);
|
||||
static void set_ea_1snh_num_samples(STREAMFILE* streamFile, off_t start_offset, ea_header* ea);
|
||||
static int get_ea_1snh_ima_version(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea);
|
||||
|
||||
/* EA 1SNh - from early EA games (~1996, ex. Need for Speed) */
|
||||
VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) {
|
||||
|
@ -37,9 +40,15 @@ VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) {
|
|||
goto fail;
|
||||
|
||||
/* check header (first block) */
|
||||
if (read_32bitBE(0,streamFile)!=0x31534E68) /* "1SNh" */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x31534E68 && /* "1SNh" */
|
||||
read_32bitBE(0x00,streamFile) != 0x53454144) /* "SEAD" */
|
||||
goto fail;
|
||||
|
||||
/* stream is divided into blocks/chunks: 1SNh=audio header, 1SNd=data xN, 1SNl=loop end, 1SNe=end.
|
||||
* Video uses various blocks (kVGT/fVGT/etc) and sometimes alt audio blocks (SEAD/SNDC/SEND). */
|
||||
|
||||
ea.is_sead = read_32bitBE(0x00,streamFile) == 0x53454144;
|
||||
|
||||
/* use block size as endian marker (Saturn = BE) */
|
||||
ea.big_endian = !(read_32bitLE(0x04,streamFile) < 0x0000FFFF);
|
||||
|
||||
|
@ -63,16 +72,22 @@ VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) {
|
|||
vgmstream->meta_type = meta_EA_1SNH;
|
||||
|
||||
switch (ea.codec) {
|
||||
case EA_CODEC_PCM:
|
||||
case EA_CODEC_PCM: /* Need for Speed (PC) */
|
||||
vgmstream->coding_type = ea.bits==1 ? coding_PCM8_int : coding_PCM16_int;
|
||||
break;
|
||||
|
||||
case EA_CODEC_IMA:
|
||||
if (ea.bits!=2) goto fail;
|
||||
vgmstream->coding_type = coding_DVI_IMA; /* stereo/mono, high nibble first */
|
||||
case EA_CODEC_ULAW: /* Crusader: No Remorse movies (SAT), FIFA 96 movies (SAT) */
|
||||
if (ea.bits && ea.bits!=2) goto fail; /* only set in EACS */
|
||||
vgmstream->coding_type = coding_ULAW_int;
|
||||
break;
|
||||
|
||||
case EA_CODEC_PSX:
|
||||
case EA_CODEC_IMA: /* Need for Speed II (PC) */
|
||||
if (ea.bits && ea.bits!=2) goto fail; /* only in EACS */
|
||||
vgmstream->coding_type = coding_DVI_IMA; /* stereo/mono, high nibble first */
|
||||
vgmstream->codec_version = ea.codec_version;
|
||||
break;
|
||||
|
||||
case EA_CODEC_PSX: /* Need for Speed (PS) */
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
break;
|
||||
|
||||
|
@ -98,7 +113,7 @@ static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t offset) {
|
|||
int32_t (*read_32bit)(off_t,STREAMFILE*) = ea->big_endian ? read_32bitBE : read_32bitLE;
|
||||
|
||||
if (read_32bitBE(offset+0x00, streamFile) == 0x45414353) { /* "EACS" */
|
||||
/* PC/SAT EACS subheader */
|
||||
/* EACS subheader (PC, SAT) */
|
||||
ea->sample_rate = read_32bit(offset+0x04, streamFile);
|
||||
ea->bits = read_8bit(offset+0x08, streamFile);
|
||||
ea->channels = read_8bit(offset+0x09, streamFile);
|
||||
|
@ -109,15 +124,32 @@ static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t offset) {
|
|||
ea->loop_end = read_32bit(offset+0x14, streamFile) + ea->loop_start; /* loop length */
|
||||
/* 0x18: data start? (0x00), 0x1c: pan/volume/etc? (0x7F), rest can be padding/garbage */
|
||||
VGM_ASSERT(ea->type != 0, "EA EACS: unknown type\n"); /* block type? */
|
||||
|
||||
if (ea->codec == EA_CODEC_IMA)
|
||||
ea->codec_version = get_ea_1snh_ima_version(streamFile, 0x00, ea);
|
||||
}
|
||||
else if (ea->is_sead) {
|
||||
/* alt subheader (found in some PC videos) */
|
||||
ea->sample_rate = read_32bit(offset+0x00, streamFile);
|
||||
ea->channels = read_32bit(offset+0x04, streamFile);
|
||||
ea->codec = read_32bit(offset+0x08, streamFile);
|
||||
|
||||
if (ea->codec == EA_CODEC_IMA)
|
||||
ea->codec_version = get_ea_1snh_ima_version(streamFile, 0x00, ea);
|
||||
|
||||
set_ea_1snh_num_samples(streamFile, 0x00, ea);
|
||||
if (ea->loop_start_offset) /* offset found, now find actual start sample */
|
||||
set_ea_1snh_num_samples(streamFile, 0x00, ea);
|
||||
}
|
||||
else {
|
||||
/* PS subheader */
|
||||
/* alt subheader (PS) */
|
||||
ea->sample_rate = read_32bit(offset+0x00, streamFile);
|
||||
ea->channels = read_8bit(offset+0x18, streamFile);
|
||||
ea->codec = EA_CODEC_PSX;
|
||||
set_ea_1snh_psx_samples(streamFile, 0x00, ea);
|
||||
if (ea->loop_start_offset)/* found offset, now find sample start */
|
||||
set_ea_1snh_psx_samples(streamFile, 0x00, ea);
|
||||
|
||||
set_ea_1snh_num_samples(streamFile, 0x00, ea);
|
||||
if (ea->loop_start_offset) /* offset found, now find actual start sample */
|
||||
set_ea_1snh_num_samples(streamFile, 0x00, ea);
|
||||
}
|
||||
|
||||
ea->loop_flag = (ea->loop_end > 0);
|
||||
|
@ -126,42 +158,61 @@ static int parse_header(STREAMFILE* streamFile, ea_header* ea, off_t offset) {
|
|||
}
|
||||
|
||||
/* get total samples by parsing block headers, needed when EACS isn't present */
|
||||
static void set_ea_1snh_psx_samples(STREAMFILE* streamFile, off_t start_offset, ea_header* ea) {
|
||||
static void set_ea_1snh_num_samples(STREAMFILE* streamFile, off_t start_offset, ea_header* ea) {
|
||||
int num_samples = 0, loop_start = 0, loop_end = 0, loop_start_offset = 0;
|
||||
off_t block_offset = start_offset;
|
||||
size_t file_size = get_streamfile_size(streamFile);
|
||||
size_t block_size, block_header, block_samples;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = ea->big_endian ? read_32bitBE : read_32bitLE;
|
||||
size_t file_size = get_streamfile_size(streamFile);
|
||||
|
||||
|
||||
while (block_offset < file_size) {
|
||||
uint32_t id = read_32bitBE(block_offset+0x00,streamFile);
|
||||
size_t block_size = read_32bit(block_offset+0x04,streamFile); /* includes id/size */
|
||||
block_size = read_32bit(block_offset+0x04,streamFile);
|
||||
block_header = 0;
|
||||
block_samples = 0;
|
||||
|
||||
if (id == 0x31534E68) { /* "1SNh" header block found */
|
||||
size_t block_header = read_32bitBE(block_offset+0x08, streamFile) == 0x45414353 ? 0x28 : 0x2c; /* "EACS" */
|
||||
if (block_header < block_size) /* sometimes has data */
|
||||
num_samples += ps_bytes_to_samples(block_size - block_header, ea->channels);
|
||||
if (id == 0x31534E68 || id == 0x53454144) { /* "1SNh" "SEAD" audio header */
|
||||
int is_sead = (id == 0x53454144);
|
||||
int is_eacs = read_32bitBE(block_offset+0x08, streamFile) == 0x45414353;
|
||||
|
||||
block_header = is_eacs ? 0x28 : (is_sead ? 0x14 : 0x2c);
|
||||
if (block_header >= block_size) /* sometimes has audio data after header */
|
||||
block_header = 0;
|
||||
}
|
||||
|
||||
if (id == 0x31534E64) { /* "1SNd" data block found */
|
||||
num_samples += ps_bytes_to_samples(block_size - 0x08, ea->channels);
|
||||
else if (id == 0x31534E64 || id == 0x534E4443) { /* "1SNd" "SNDC" audio data */
|
||||
block_header = 0x08;
|
||||
}
|
||||
|
||||
if (id == 0x31534E6C) { /* "1SNl" loop point found */
|
||||
else if (id == 0x00000000) {
|
||||
break;
|
||||
}
|
||||
else if (id == 0x31534E6C) { /* "1SNl" loop point found */
|
||||
loop_start_offset = read_32bit(block_offset+0x08,streamFile);
|
||||
loop_end = num_samples;
|
||||
}
|
||||
|
||||
if (id == 0x00000000 || id == 0xFFFFFFFF) { /* EOF: possible? */
|
||||
break;
|
||||
if (block_header) {
|
||||
switch(ea->codec) {
|
||||
case EA_CODEC_PSX:
|
||||
block_samples = ps_bytes_to_samples(block_size - block_header, ea->channels);
|
||||
break;
|
||||
case EA_CODEC_IMA:
|
||||
if (ea->codec_version == 1)
|
||||
block_samples = read_32bit(block_offset + block_header, streamFile);
|
||||
else
|
||||
block_samples = ima_bytes_to_samples(block_size - block_header, ea->channels);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if there is a loop start offset this was called again just to find it */
|
||||
|
||||
/* if there is a loop start offset set, this was called again just to find it */
|
||||
if (ea->loop_start_offset && ea->loop_start_offset == block_offset) {
|
||||
ea->loop_start = num_samples;
|
||||
return;
|
||||
}
|
||||
|
||||
/* any other blocks "1SNl" "1SNe" etc */ //todo parse movie blocks
|
||||
num_samples += block_samples;
|
||||
block_offset += block_size;
|
||||
}
|
||||
|
||||
|
@ -171,3 +222,31 @@ static void set_ea_1snh_psx_samples(STREAMFILE* streamFile, off_t start_offset,
|
|||
ea->loop_end = loop_end;
|
||||
ea->loop_start_offset = loop_start_offset;
|
||||
}
|
||||
|
||||
/* find codec version used, with or without ADPCM hist per block */
|
||||
static int get_ea_1snh_ima_version(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea) {
|
||||
off_t block_offset = start_offset;
|
||||
size_t file_size = get_streamfile_size(streamFile);
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = ea->big_endian ? read_32bitBE : read_32bitLE;
|
||||
|
||||
while (block_offset < file_size) {
|
||||
uint32_t id = read_32bitBE(block_offset+0x00,streamFile);
|
||||
|
||||
size_t block_size = read_32bitLE(block_offset+0x04,streamFile);
|
||||
if (block_size > 0x00F00000) /* BE in SAT, but one file may have both BE and LE chunks */
|
||||
block_size = read_32bitBE(block_offset+0x04,streamFile);
|
||||
|
||||
if (id == 0x31534E64 || id == 0x534E4443) { /* "1SNd" "SNDC" audio data */
|
||||
size_t ima_samples = read_32bit(block_offset + 0x08, streamFile);
|
||||
size_t expected_samples = (block_size - 0x08 - 0x04 - 0x08*ea->channels) * 2 / ea->channels;
|
||||
|
||||
if (ima_samples == expected_samples) {
|
||||
return 1; /* has ADPCM hist (hopefully) */
|
||||
}
|
||||
}
|
||||
|
||||
block_offset += block_size;
|
||||
}
|
||||
|
||||
return 0; /* no ADPCM hist */
|
||||
}
|
||||
|
|
|
@ -214,18 +214,36 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
|
|||
mpeg_custom_t type = (codec == 0x05 ? MPEG_EAL31b : (codec == 0x06) ? MPEG_EAL32P : MPEG_EAL32S);
|
||||
|
||||
/* layout is still blocks, but should work fine with the custom mpeg decoder */
|
||||
vgmstream->codec_data = init_mpeg_custom_codec_data(streamData, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, type, &cfg);
|
||||
vgmstream->codec_data = init_mpeg_custom(streamData, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, type, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
vgmstream->layout_type = layout_blocked_ea_sns;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0 //todo unknown variation
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case 0x0a: { /* EATrax */
|
||||
atrac9_config cfg = {0};
|
||||
|
||||
cfg.channels = vgmstream->channels;
|
||||
cfg.config_data = read_32bitBE(header_offset + 0x08,streamHead);
|
||||
/* 0x0c: data size without blocks?, 0x10: frame size? (same as config data?) */
|
||||
|
||||
vgmstream->codec_data = init_atrac9(&cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_ATRAC9;
|
||||
vgmstream->layout_type = layout_blocked_ea_sns;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
case 0x00: /* "NONE" (internal 'codec not set' flag) */
|
||||
case 0x01: /* not used/reserved? Gca0/MP30/P6L0/P2B0/P2L0/P8S0/P8U0/PFN0? */
|
||||
case 0x08: /* ? */
|
||||
case 0x09: /* EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) */
|
||||
case 0x0a: /* EATrax (ATRAC9 variant, has deflated fillers a la EA-XMA) */
|
||||
case 0x0b: /* ? */
|
||||
case 0x0c: /* EAOpus (inside each SNS/SPS block is 16b frame size + standard? Opus packet) */
|
||||
case 0x0d: /* ? */
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#define EA_CODEC1_NONE -1
|
||||
#define EA_CODEC1_PCM 0x00
|
||||
#define EA_CODEC1_VAG 0x01 // unsure
|
||||
#define EA_CODEC1_EAXA 0x07 // Need for Speed 2 PC, Fifa 98 SAT
|
||||
#define EA_CODEC1_EAXA 0x07 // Need for Speed 2 PC, FIFA 98 SAT
|
||||
#define EA_CODEC1_MT10 0x09
|
||||
//#define EA_CODEC1_N64 ?
|
||||
|
||||
|
@ -91,12 +91,14 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) {
|
|||
goto fail;
|
||||
|
||||
/* check header */
|
||||
/* EA's stream files are made of blocks called "chunks" (SCxx, presumably Sound Chunk xx)
|
||||
* typically: SCHl=header, SCCl=count of SCDl, SCDl=data xN, SCLl=loop end, SCEl=stream end.
|
||||
* The number/size of blocks is affected by: block rate setting, sample rate, channels, CPU location (SPU/main/DSP/others), etc */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x5343486C) /* "SCHl" */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x5343486C && /* "SCHl" */
|
||||
read_32bitBE(0x00,streamFile) != 0x5348454E) /* "SHEN" */
|
||||
goto fail;
|
||||
|
||||
/* stream is divided into blocks/chunks: SCHl=audio header, SCCl=count of SCDl, SCDl=data xN, SCLl=loop end, SCEl=end.
|
||||
* Video uses various blocks (MVhd/MV0K/etc) and sometimes alt audio blocks (SHEN/SDEN/SEEN).
|
||||
* The number/size is affected by: block rate setting, sample rate, channels, CPU location (SPU/main/DSP/others), etc */
|
||||
|
||||
header_size = read_32bitLE(0x04,streamFile);
|
||||
if (header_size > 0x00F00000) /* size is always LE, except in early SS/MAC */
|
||||
header_size = read_32bitBE(0x04,streamFile);
|
||||
|
@ -318,7 +320,7 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
|
|||
if (!mpeg_start_offset) goto fail;
|
||||
|
||||
/* layout is still blocks, but should work fine with the custom mpeg decoder */
|
||||
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EA, &cfg);
|
||||
vgmstream->codec_data = init_mpeg_custom(streamFile, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EA, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
break;
|
||||
}
|
||||
|
@ -331,7 +333,7 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
|
|||
if (!mpeg_start_offset) goto fail;
|
||||
|
||||
/* layout is still blocks, but should work fine with the custom mpeg decoder */
|
||||
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EAL31, &cfg);
|
||||
vgmstream->codec_data = init_mpeg_custom(streamFile, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EAL31, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
break;
|
||||
}
|
||||
|
@ -451,7 +453,7 @@ static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t be
|
|||
memset(ea,0,sizeof(ea_header));
|
||||
|
||||
/* null defaults as 0 can be valid */
|
||||
ea->version = EA_VERSION_NONE;
|
||||
ea->version = EA_VERSION_NONE;
|
||||
ea->codec1 = EA_CODEC1_NONE;
|
||||
ea->codec2 = EA_CODEC2_NONE;
|
||||
|
||||
|
@ -747,70 +749,73 @@ fail:
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* get total samples by parsing block headers, needed when multiple files are stitched together */
|
||||
/* Some EA files (.mus, .eam, .sng, etc) concat many small subfiles, used as mapped
|
||||
* music (.map/lin). We get total possible samples (counting all subfiles) and pretend
|
||||
* they are a single stream. Subfiles always share header, except num_samples. */
|
||||
/* Get total samples by parsing block headers, needed when multiple files are stitched together.
|
||||
* Some EA files (.mus/eam/sng/etc) concat many small subfiles, used for interactive/mapped
|
||||
* music (.map/lin). Subfiles always share header, except num_samples. */
|
||||
static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea) {
|
||||
int num_samples = 0;
|
||||
size_t file_size = get_streamfile_size(streamFile);
|
||||
int new_schl = 0;
|
||||
off_t block_offset = start_offset;
|
||||
size_t block_size, block_samples;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = ea->big_endian ? read_32bitBE : read_32bitLE;
|
||||
size_t file_size = get_streamfile_size(streamFile);
|
||||
|
||||
|
||||
while (block_offset < file_size) {
|
||||
uint32_t id, block_size, block_samples;
|
||||
|
||||
id = read_32bitBE(block_offset+0x00,streamFile);
|
||||
uint32_t id = read_32bitBE(block_offset+0x00,streamFile);
|
||||
|
||||
block_size = read_32bitLE(block_offset+0x04,streamFile);
|
||||
if (block_size > 0x00F00000) /* size is always LE, except in early SS/MAC */
|
||||
if (block_size > 0x00F00000) /* size is always LE, except in early SAT/MAC */
|
||||
block_size = read_32bitBE(block_offset+0x04,streamFile);
|
||||
|
||||
/* SCxx blocks have size in the header, but others may not. To simplify we just try to
|
||||
* find a SCDl (main data) every 0x04. EA sometimes concats many small files, so after a SCEl (end block)
|
||||
* there may be a new SCHl + SCDl too, so this pretends they are a single stream. */
|
||||
if (id == 0x5343446C) { /* "SCDl" data block found */
|
||||
block_samples = 0;
|
||||
|
||||
/* use num_samples from header if possible */
|
||||
if (id == 0x5343446C || id == 0x5344454E) { /* "SCDl" "SDEN" audio data */
|
||||
switch (ea->codec2) {
|
||||
case EA_CODEC2_VAG: /* PS-ADPCM */
|
||||
case EA_CODEC2_VAG:
|
||||
block_samples = ps_bytes_to_samples(block_size-0x10, ea->channels);
|
||||
break;
|
||||
|
||||
default:
|
||||
block_samples = read_32bit(block_offset+0x08,streamFile);
|
||||
break;
|
||||
}
|
||||
|
||||
/* guard against false positives (happens in "pIQT" blocks) */
|
||||
if (block_size > 0xFFFF || block_samples > 0xFFFF) { /* observed max is ~0xf00 but who knows */
|
||||
block_offset += 0x04;
|
||||
continue;
|
||||
}
|
||||
|
||||
num_samples += block_samples;
|
||||
|
||||
block_offset += block_size; /* size includes header */
|
||||
}
|
||||
else {
|
||||
/* movie "pIQT" may be bigger than what block_size says, but seems to help */
|
||||
if (id == 0x5343486C || id == 0x5343436C || id == 0x53434C6C || id == 0x70495154) { /* "SCHl" "SCCl" "SCLl" "SCEl" "pIQT" */
|
||||
block_offset += block_size;
|
||||
} else {
|
||||
block_offset += 0x04;
|
||||
else { /* any other chunk, audio ("SCHl" "SCCl" "SCLl" "SCEl" etc), or video ("pQGT" "pIQT "MADk" "MPCh" etc) */
|
||||
/* padding between "SCEl" and next "SCHl" (when subfiles exist) */
|
||||
if (id == 0x00000000) {
|
||||
block_size = 0x04;
|
||||
}
|
||||
|
||||
if (id == 0x5343456C) { /* "SCEl" end block found */
|
||||
/* Usually there is padding between SCEl and SCHl (aligned to 0x80) */
|
||||
block_offset += (block_offset % 0x04) == 0 ? 0 : 0x04 - (block_offset % 0x04); /* also 32b-aligned, important */
|
||||
if (id == 0x5343486C || id == 0x5348454E) { /* "SCHl" "SHEN" end block */
|
||||
new_schl = 1;
|
||||
}
|
||||
}
|
||||
|
||||
block_offset += 0x04;
|
||||
continue;
|
||||
/* guard against errors (happens in bad rips/endianness, observed max is vid ~0x20000) */
|
||||
if (block_size == 0x00 || block_size > 0xFFFFF || block_samples > 0xFFFF) {
|
||||
VGM_LOG("EA SCHl: bad block size %x at %lx\n", block_size, block_offset);
|
||||
block_size = 0x04;
|
||||
block_samples = 0;
|
||||
}
|
||||
|
||||
num_samples += block_samples;
|
||||
block_offset += block_size;
|
||||
|
||||
/* "SCEl" are aligned to 0x80 usually, but causes problems if not 32b-aligned (ex. Need for Speed 2 PC) */
|
||||
if ((id == 0x5343456C || id == 0x5345454E) && block_offset % 0x04) {
|
||||
VGM_LOG_ONCE("EA SCHl: mis-aligned end offset found\n");
|
||||
block_offset += 0x04 - (block_offset % 0x04);
|
||||
}
|
||||
}
|
||||
|
||||
return num_samples;
|
||||
/* only use calculated samples with multiple subfiles (rarely header samples may be less due to padding) */
|
||||
if (new_schl) {
|
||||
;VGM_LOG("EA SCHl: multiple SCHl found\n");
|
||||
return num_samples;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* find data start offset inside the first SCDl; not very elegant but oh well */
|
||||
|
@ -825,7 +830,7 @@ static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start
|
|||
id = read_32bitBE(block_offset+0x00,streamFile);
|
||||
|
||||
block_size = read_32bitLE(block_offset+0x04,streamFile);
|
||||
if (block_size > 0x00F00000) /* size is always LE, except in early SS/MAC */
|
||||
if (block_size > 0x00F00000) /* size is always LE, except in early SAT/MAC */
|
||||
block_size = read_32bitBE(block_offset+0x04,streamFile);
|
||||
|
||||
if (id == 0x5343446C) { /* "SCDl" data block found */
|
||||
|
|
|
@ -296,7 +296,7 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
|
|||
//VGM_ASSERT(fsbh.mode & FSOUND_MPEG_LAYER2, "FSB FSOUND_MPEG_LAYER2 found\n");/* not always set anyway */
|
||||
VGM_ASSERT(fsbh.mode & FSOUND_IGNORETAGS, "FSB FSOUND_IGNORETAGS found\n");
|
||||
|
||||
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg);
|
||||
vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
|
|
|
@ -6,37 +6,42 @@
|
|||
VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t StartOffset = 0, NameOffset = 0;
|
||||
off_t SampleHeaderStart = 0, DSPInfoStart = 0;
|
||||
size_t SampleHeaderLength, NameTableLength, SampleDataLength, BaseHeaderLength, StreamSize = 0;
|
||||
off_t SampleHeaderStart = 0, ExtraInfoStart = 0;
|
||||
size_t SampleHeaderLength, NameTableLength, SampleDataLength, BaseHeaderLength, StreamSize = 0, ExtraInfoSize = 0;
|
||||
|
||||
uint32_t NumSamples = 0, LoopStart = 0, LoopEnd = 0;
|
||||
int LoopFlag = 0, ChannelCount = 0, SampleRate = 0, CodingID;
|
||||
int LoopFlag = 0, ChannelCount = 0, Version, SampleRate = 0, CodingID;
|
||||
int TotalStreams, TargetStream = streamFile->stream_index;
|
||||
uint32_t VorbisSetupId = 0;
|
||||
int i;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
if (!check_extensions(streamFile,"fsb")) goto fail;
|
||||
if (!check_extensions(streamFile,"fsb"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x46534235) goto fail; /* "FSB5" */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x46534235) /* "FSB5" */
|
||||
goto fail;
|
||||
|
||||
//v0 has extra flags at 0x1c and BaseHeaderLength = 0x40?
|
||||
if (read_32bitLE(0x04,streamFile) != 0x01) goto fail; /* Version ID */
|
||||
/* 0x00 is rare (seen in Tales from Space Vita) */
|
||||
Version = read_32bitLE(0x04,streamFile);
|
||||
if (Version != 0x00 && Version != 0x01) goto fail;
|
||||
|
||||
TotalStreams = read_32bitLE(0x08,streamFile);
|
||||
SampleHeaderLength = read_32bitLE(0x0C,streamFile);
|
||||
NameTableLength = read_32bitLE(0x10,streamFile);
|
||||
SampleDataLength = read_32bitLE(0x14,streamFile);
|
||||
CodingID = read_32bitLE(0x18,streamFile);
|
||||
/* 0x1c (8): zero, 0x24 (16): hash, 0x34 (8): unk */
|
||||
BaseHeaderLength = 0x3C;
|
||||
/* type 0x01 - 0x1c(8): zero, 0x24(16): hash, 0x34(8): unk
|
||||
* type 0x00 has an extra field (always 0?) at 0x1c */
|
||||
BaseHeaderLength = (Version==0x00) ? 0x40 : 0x3C;
|
||||
|
||||
SampleHeaderStart = BaseHeaderLength;
|
||||
if ((SampleHeaderLength + NameTableLength + SampleDataLength + BaseHeaderLength) != get_streamfile_size(streamFile))
|
||||
goto fail;
|
||||
|
||||
if ((SampleHeaderLength + NameTableLength + SampleDataLength + 0x3C) != get_streamfile_size(streamFile)) goto fail;
|
||||
if (TargetStream == 0) TargetStream = 1; /* default to 1 */
|
||||
if (TargetStream > TotalStreams || TotalStreams <= 0) goto fail;
|
||||
|
||||
SampleHeaderStart = BaseHeaderLength;
|
||||
|
||||
/* find target stream header and data offset, and read all needed values for later use
|
||||
* (reads one by one as the size of a single stream header is variable) */
|
||||
for (i = 1; i <= TotalStreams; i++) {
|
||||
|
@ -113,18 +118,23 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
|
|||
break;
|
||||
case 0x04: /* free comment, or maybe SFX info */
|
||||
break;
|
||||
//case 0x05: /* Unknown (32b) */
|
||||
// /* found in Tearaway Vita, value 0, first stream only */
|
||||
// break;
|
||||
case 0x06: /* XMA seek table */
|
||||
/* no need for it */
|
||||
break;
|
||||
case 0x07: /* DSP Info (Coeffs) */
|
||||
DSPInfoStart = ExtraFlagStart + 0x04;
|
||||
case 0x07: /* DSP coeffs */
|
||||
ExtraInfoStart = ExtraFlagStart + 0x04;
|
||||
break;
|
||||
case 0x09: /* ATRAC9 data */
|
||||
case 0x09: /* ATRAC9 config */
|
||||
ExtraInfoStart = ExtraFlagStart + 0x04;
|
||||
ExtraInfoSize = ExtraFlagSize;
|
||||
break;
|
||||
case 0x0a: /* XWMA data */
|
||||
break;
|
||||
case 0x0b: /* Vorbis data */
|
||||
VorbisSetupId = (uint32_t)read_32bitLE(ExtraFlagStart+0x04,streamFile); /* crc32? */
|
||||
case 0x0b: /* Vorbis setup ID and seek table */
|
||||
ExtraInfoStart = ExtraFlagStart + 0x04;
|
||||
/* seek table format:
|
||||
* 0x08: table_size (total_entries = seek_table_size / (4+4)), not counting this value; can be 0
|
||||
* 0x0C: sample number (only some samples are saved in the table)
|
||||
|
@ -132,7 +142,8 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
|
|||
* (xN entries)
|
||||
*/
|
||||
break;
|
||||
//case 0x0d: /* Unknown value (32b), found in some XMA2 and Vorbis */
|
||||
//case 0x0d: /* Unknown (32b) */
|
||||
// /* found in some XMA2 and Vorbis */
|
||||
// break;
|
||||
default:
|
||||
VGM_LOG("FSB5: unknown extra flag 0x%x at 0x%04x (size 0x%x)\n", ExtraFlagType, ExtraFlagStart, ExtraFlagSize);
|
||||
|
@ -221,7 +232,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
|
|||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->interleave_block_size = 0x02;
|
||||
|
||||
dsp_read_coefs_be(vgmstream,streamFile,DSPInfoStart,0x2E);
|
||||
dsp_read_coefs_be(vgmstream,streamFile,ExtraInfoStart,0x2E);
|
||||
break;
|
||||
|
||||
case 0x07: /* FMOD_SOUND_FORMAT_IMAADPCM */
|
||||
|
@ -263,7 +274,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
|
|||
|
||||
cfg.fsb_padding = (vgmstream->channels > 2 ? 16 : 4); /* observed default */
|
||||
|
||||
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, StartOffset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg);
|
||||
vgmstream->codec_data = init_mpeg_custom(streamFile, StartOffset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
|
@ -272,8 +283,36 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
|
|||
case 0x0C: /* FMOD_SOUND_FORMAT_CELT */
|
||||
goto fail;
|
||||
|
||||
case 0x0D: /* FMOD_SOUND_FORMAT_AT9 */
|
||||
goto fail;
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case 0x0D: {/* FMOD_SOUND_FORMAT_AT9 */
|
||||
atrac9_config cfg = {0};
|
||||
|
||||
cfg.channels = vgmstream->channels;
|
||||
switch(ExtraInfoSize) {
|
||||
case 0x04: /* Little Big Planet 2ch (Vita), Guacamelee (Vita) */
|
||||
cfg.config_data = read_32bitBE(ExtraInfoStart,streamFile);
|
||||
break;
|
||||
case 0x08: /* Day of the Tentacle Remastered (Vita) */
|
||||
/* 0x00: superframe size (also in config_data) */
|
||||
cfg.config_data = read_32bitBE(ExtraInfoStart+0x04,streamFile);
|
||||
break;
|
||||
//case 0x0c: /* Little Big Planet 6ch (Vita) */
|
||||
// //todo: this is just 0x04 x3, in case of 4ch would be 0x08 --must improve detection
|
||||
// //each stream has its own config_data (but seem to be the same), interleaves 1 super frame per stream
|
||||
// break;
|
||||
default:
|
||||
VGM_LOG("FSB5: unknown extra info size 0x%x\n", ExtraInfoSize);
|
||||
goto fail;
|
||||
}
|
||||
//cfg.encoder_delay = 0x100; //todo not used? num_samples seems to count all data
|
||||
|
||||
vgmstream->codec_data = init_atrac9(&cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_ATRAC9;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case 0x0E: /* FMOD_SOUND_FORMAT_XWMA */
|
||||
goto fail;
|
||||
|
@ -284,11 +323,11 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
|
|||
|
||||
cfg.channels = vgmstream->channels;
|
||||
cfg.sample_rate = vgmstream->sample_rate;
|
||||
cfg.setup_id = VorbisSetupId;
|
||||
cfg.setup_id = read_32bitLE(ExtraInfoStart,streamFile);
|
||||
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->coding_type = coding_VORBIS_custom;
|
||||
vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, StartOffset, VORBIS_FSB, &cfg);
|
||||
vgmstream->codec_data = init_vorbis_custom(streamFile, StartOffset, VORBIS_FSB, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
break;
|
||||
|
|
|
@ -239,7 +239,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
|
|||
#ifdef VGM_USE_MPEG
|
||||
case coding_MPEG_layer3:
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->codec_data = init_mpeg_codec_data(streamFile, genh.start_offset, &coding, vgmstream->channels);
|
||||
vgmstream->codec_data = init_mpeg(streamFile, genh.start_offset, &coding, vgmstream->channels);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
break;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
typedef enum { XMA2 } gtd_codec;
|
||||
typedef enum { XMA2, ATRAC9 } gtd_codec;
|
||||
|
||||
/* GTD - found in Knights Contract (X360, PS3), Valhalla Knights 3 (PSV) */
|
||||
VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) {
|
||||
|
@ -10,6 +10,7 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) {
|
|||
size_t data_size, chunk_size;
|
||||
int loop_flag, channel_count, sample_rate;
|
||||
int num_samples, loop_start_sample, loop_end_sample;
|
||||
uint32_t at9_config_data;
|
||||
gtd_codec codec;
|
||||
|
||||
|
||||
|
@ -34,30 +35,41 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) {
|
|||
data_size = read_32bitBE(0x5c,streamFile);
|
||||
/* 0x34(18): null, 0x54(4): seek table offset, 0x58(4): seek table size, 0x5c(8): null, 0x64: seek table */
|
||||
|
||||
stpr_offset = read_32bitBE(chunk_offset+0x54,streamFile) + read_32bitBE(chunk_offset+0x58,streamFile);;
|
||||
stpr_offset = read_32bitBE(chunk_offset+0x54,streamFile) + read_32bitBE(chunk_offset+0x58,streamFile);
|
||||
if (read_32bitBE(stpr_offset,streamFile) == 0x53545052) { /* "STPR" */
|
||||
name_offset = stpr_offset + 0xB8; /* there are offsets fields but seems to work */
|
||||
}
|
||||
|
||||
codec = XMA2;
|
||||
}
|
||||
else if (0x34 + read_32bitLE(0x30,streamFile) + read_32bitLE(0x0c,streamFile) == get_streamfile_size(streamFile)) { /* ATRAC9 */
|
||||
|
||||
data_size = read_32bitLE(0x0c,streamFile);
|
||||
start_offset = 0x34 + read_32bitLE(0x30,streamFile);
|
||||
channel_count = read_32bitLE(0x10,streamFile);
|
||||
sample_rate = read_32bitLE(0x14,streamFile);
|
||||
at9_config_data = read_32bitBE(0x28,streamFile);
|
||||
/* 0x18-0x28: fixed/unknown values */
|
||||
|
||||
stpr_offset = 0x2c;
|
||||
if (read_32bitBE(stpr_offset,streamFile) == 0x53545052) { /* "STPR" */
|
||||
name_offset = stpr_offset + 0xE8; /* there are offsets fields but seems to work */
|
||||
}
|
||||
|
||||
codec = ATRAC9;
|
||||
}
|
||||
else {
|
||||
/* there are PSV (LE, ATRAC9 data) and PS3 (MSF inside?) variations, somewhat-but-not-quite similar */
|
||||
|
||||
/* for PSV: */
|
||||
/* 0x0c: data_size, 0x10: channles, 0x14: sample rate, 0x18-0x2c: fixed and unknown values */
|
||||
/* 0x2c: STPR chunk, with name_offset at + 0xE8 */
|
||||
|
||||
/* apparently there is a PS3 variation (MSF inside?) */
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start_sample;
|
||||
vgmstream->loop_end_sample = loop_end_sample;
|
||||
vgmstream->meta_type = meta_GTD;
|
||||
|
@ -77,9 +89,29 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) {
|
|||
if ( !vgmstream->codec_data ) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = num_samples;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case ATRAC9: {
|
||||
atrac9_config cfg = {0};
|
||||
|
||||
cfg.channels = vgmstream->channels;
|
||||
cfg.config_data = at9_config_data;
|
||||
|
||||
vgmstream->codec_data = init_atrac9(&cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_ATRAC9;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = atrac9_bytes_to_samples(data_size, vgmstream->codec_data);
|
||||
break;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
|
|
@ -147,7 +147,81 @@ static const hcakey_info hcakey_list[] = {
|
|||
// Kirara Fantasia (Android/iOS)
|
||||
{51408295487268137}, // 00B6A3928706E529
|
||||
|
||||
// A3! (iOS/Android)
|
||||
{914306251}, // 00000000367F34CB
|
||||
|
||||
// Weekly Shonen Jump: Ore Collection! (iOS/Android)
|
||||
{11708691}, // 0000000000B2A913
|
||||
|
||||
// Monster Gear Versus (iOS/Android)
|
||||
{0xB1E30F346415B475}, // B1E30F346415B475
|
||||
|
||||
// Yumeiro Cast (iOS/Android)
|
||||
{14418}, // 0000000000003852
|
||||
|
||||
// Ikki Tousen: Straight Striker (iOS/Android)
|
||||
{1000}, // 00000000000003E8
|
||||
|
||||
// Zero kara Hajimeru Mahou no Sho (iOS/Android)
|
||||
{0xD2E836E662F20000}, // D2E836E662F20000
|
||||
|
||||
// Soul Reverse Zero (iOS/Android)
|
||||
{2873513618}, // 00000000AB465692
|
||||
|
||||
// Jojo's Bizarre Adventure: Diamond Records (iOS/Android) [additional data]
|
||||
{0x820212864CAB35DE}, // 820212864CAB35DE
|
||||
|
||||
// HUNTER x HUNTER: World Hunt (iOS/Android)
|
||||
{71777214294589695}, // 00FF00FF00FF00FF
|
||||
|
||||
// \Comepri/ Comedy Prince (iOS/Android)
|
||||
{201537197868}, // 0000002EEC8D972C
|
||||
|
||||
// Puzzle of Empires (iOS/Android)
|
||||
{13687846}, // 0000000000D0DC26
|
||||
|
||||
// Aozora Under Girls! (iOS/Android)
|
||||
{4988006236073}, // 000004895C56FFA9
|
||||
|
||||
// Castle & Dragon (iOS/Android)
|
||||
{20140528}, // 00000000013351F0
|
||||
|
||||
// Uta no Prince sama Shining Live (iOS/Android)
|
||||
{2122831366}, // 000000007E87D606
|
||||
|
||||
// Sevens Story (iOS/Android)
|
||||
{629427372852}, // 000000928CCB8334
|
||||
|
||||
// MinGol: Everybody's Golf (iOS/Android)
|
||||
{1430028151061218}, // 0005149A5FF67AE2
|
||||
|
||||
// AKB48 Group Tsui ni Koushiki Otoge demashita. (iOS/Android)
|
||||
{831021912315111419}, // 0B886206BC1BA7FB
|
||||
|
||||
// Sen no Kaizoku (iOS/Android)
|
||||
{81368371967}, // 00000012F1EED2FF
|
||||
|
||||
// I Chu (iOS/Android)
|
||||
{13456}, // 0000000000003490
|
||||
|
||||
// Shinobi Nightmare (iOS/Android)
|
||||
{369481198260487572}, // 0520A93135808594
|
||||
|
||||
// Bungo Stray Dogs: Mayoi Inu Kaikitan (iOS/Android)
|
||||
{1655728931134731873}, // 16FA54B0C09F7661
|
||||
|
||||
// Super Sentai Legend Wars (iOS/Android)
|
||||
{4017992759667450}, // 000E4657D7266AFA
|
||||
|
||||
// Metal Saga: The Ark of Wastes (iOS/Android)
|
||||
{100097101118097115}, // 01639DC87B30C6DB
|
||||
|
||||
// Taga Tame no Alchemist (iOS/Android)
|
||||
{5047159794308}, // 00000497222AAA84
|
||||
|
||||
// Shin Tennis no Ouji-sama: Rising Beat (iOS/Android)
|
||||
{4902201417679}, // 0000047561F95FCF
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif/*_HCA_KEYS_H_*/
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../stack_alloc.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
int loop_flag, channel_count;
|
||||
int32_t loop_length;
|
||||
off_t start_offset;
|
||||
|
||||
if (!check_extensions(streamFile, "ktss"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0, streamFile) != 0x4B545353) /* "KTSS" */
|
||||
goto fail;
|
||||
|
||||
loop_length = read_32bitLE(0x38, streamFile);
|
||||
loop_flag = loop_length > 0;
|
||||
channel_count = read_8bit(0x29, streamFile);
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->num_samples = read_32bitLE(0x30, streamFile);
|
||||
vgmstream->sample_rate = (uint16_t)read_16bitLE(0x2c, streamFile);
|
||||
vgmstream->loop_start_sample = read_32bitLE(0x34, streamFile);
|
||||
vgmstream->loop_end_sample = vgmstream->loop_start_sample + loop_length;
|
||||
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->meta_type = meta_KTSS;
|
||||
|
||||
vgmstream->interleave_block_size = 0x8;
|
||||
|
||||
dsp_read_coefs_le(vgmstream, streamFile, 0x40, 0x2e);
|
||||
start_offset = read_32bitLE(0x24, streamFile) + 0x20;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -152,7 +152,7 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE * streamFile);
|
|||
|
||||
VGMSTREAM * init_vgmstream_rifx(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_xnbm(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_xnb(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_pos(STREAMFILE * streamFile);
|
||||
|
||||
|
@ -602,6 +602,7 @@ VGMSTREAM * init_vgmstream_bfwav(STREAMFILE* streamFile);
|
|||
|
||||
VGMSTREAM * init_vgmstream_kt_g1l(STREAMFILE* streamFile);
|
||||
VGMSTREAM * init_vgmstream_kt_wiibgm(STREAMFILE* streamFile);
|
||||
VGMSTREAM * init_vgmstream_ktss(STREAMFILE* streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_mca(STREAMFILE* streamFile);
|
||||
|
||||
|
@ -642,6 +643,7 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile);
|
|||
|
||||
VGMSTREAM * init_vgmstream_ta_aac_x360(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_ta_aac_ps3(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_ta_aac_mobile(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps3_mta2(STREAMFILE *streamFile);
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ VGMSTREAM * init_vgmstream_ngc_vid1(STREAMFILE *streamFile) {
|
|||
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->coding_type = coding_VORBIS_custom;
|
||||
vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, header_offset + 0x20, VORBIS_VID1, &cfg);
|
||||
vgmstream->codec_data = init_vorbis_custom(streamFile, header_offset + 0x20, VORBIS_VID1, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
}
|
||||
#else
|
||||
|
|
|
@ -189,7 +189,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
|
|||
strcasecmp("ogg",filename_extension(filename))) {
|
||||
if (!strcasecmp("um3",filename_extension(filename))) {
|
||||
um3_ogg = 1;
|
||||
} else if (!strcasecmp("kovs",filename_extension(filename))) {
|
||||
} else if (check_extensions(streamFile,"kvs,kovs")) { /* .kvs: Atelier Sophie, kovs: header id only? */
|
||||
kovs_ogg = 1;
|
||||
} else {
|
||||
goto fail;
|
||||
|
|
|
@ -46,7 +46,7 @@ VGMSTREAM * init_vgmstream_ogl(STREAMFILE *streamFile) {
|
|||
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->coding_type = coding_VORBIS_custom;
|
||||
vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, 0x14, VORBIS_OGL, &cfg);
|
||||
vgmstream->codec_data = init_vorbis_custom(streamFile, 0x14, VORBIS_OGL, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
start_offset = cfg.data_start_offset;
|
||||
|
|
|
@ -151,7 +151,7 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
|
|||
cfg.data_size = data_size;
|
||||
/* block_size * 3 = frame size (0x60*3=0x120 or 0x40*3=0xC0) but doesn't seem to have any significance) */
|
||||
|
||||
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_P3D, &cfg);
|
||||
vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_P3D, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
|
|
|
@ -173,7 +173,7 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) {
|
|||
mpeg_codec_data *mpeg_data = NULL;
|
||||
coding_t ct;
|
||||
|
||||
mpeg_data = init_mpeg_codec_data(streamFile, start_offset, &ct, vgmstream->channels);
|
||||
mpeg_data = init_mpeg(streamFile, start_offset, &ct, vgmstream->channels);
|
||||
if (!mpeg_data) goto fail;
|
||||
vgmstream->codec_data = mpeg_data;
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -37,7 +37,7 @@ VGMSTREAM * init_vgmstream_sk_aud(STREAMFILE *streamFile) {
|
|||
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->coding_type = coding_VORBIS_custom;
|
||||
vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, 0x00, VORBIS_SK, &cfg);
|
||||
vgmstream->codec_data = init_vorbis_custom(streamFile, 0x00, VORBIS_SK, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
start_offset = cfg.data_start_offset;
|
||||
|
|
|
@ -211,7 +211,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
|
|||
cfg.interleave = 0x800; /* for multistream [Final Fantasy XIII-2 (PS3)], otherwise ignored */
|
||||
cfg.data_size = stream_size;
|
||||
|
||||
mpeg_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_SCD, &cfg);
|
||||
mpeg_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_SCD, &cfg);
|
||||
if (!mpeg_data) goto fail;
|
||||
vgmstream->codec_data = mpeg_data;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* SXD - Sony's SDK format? (cousin of SGXD) [Gravity Rush, Freedom Wars, Soul Sacrifice PSV] */
|
||||
/* SXD - Sony/SCE's SNDX lib format (cousin of SGXD) [Gravity Rush, Freedom Wars, Soul Sacrifice PSV] */
|
||||
VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
STREAMFILE * streamHeader = NULL;
|
||||
off_t start_offset, chunk_offset, first_offset = 0x60, name_offset = 0;
|
||||
size_t chunk_size;
|
||||
|
||||
int is_separate;
|
||||
int loop_flag, channels, type;
|
||||
int loop_flag, channels, codec;
|
||||
int sample_rate, num_samples, loop_start_sample, loop_end_sample;
|
||||
uint32_t at9_config_data = 0;
|
||||
int total_streams, target_stream = streamFile->stream_index;
|
||||
|
||||
|
||||
|
@ -33,8 +35,7 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
|
|||
|
||||
|
||||
/* typical chunks: NAME, WAVE and many control chunks (SXDs don't need to contain any sound data) */
|
||||
/* WAVE chunk (0 + streams + 4*streams table + streams * variable? + optional padding) */
|
||||
if (!find_chunk_le(streamHeader, 0x57415645,first_offset,0, &chunk_offset,NULL)) goto fail; /* "WAVE" */
|
||||
if (!find_chunk_le(streamHeader, 0x57415645,first_offset,0, &chunk_offset,&chunk_size)) goto fail; /* "WAVE" */
|
||||
|
||||
/* check multi-streams (usually only in SFX containers) */
|
||||
total_streams = read_32bitLE(chunk_offset+0x04,streamHeader);
|
||||
|
@ -43,53 +44,60 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
|
|||
|
||||
/* read stream header */
|
||||
{
|
||||
off_t table_offset, header_offset, stream_offset, data_offset;
|
||||
int i;
|
||||
off_t table_offset, header_offset, stream_offset;
|
||||
|
||||
/* get target offset using table of relative offsets within WAVE */
|
||||
table_offset = chunk_offset + 0x08 + 4*(target_stream-1);
|
||||
header_offset = table_offset + read_32bitLE(table_offset,streamHeader);
|
||||
|
||||
type = read_32bitLE(header_offset+0x00,streamHeader);
|
||||
/* 0x04 (1): unk (HEVAG: 21, ATRAC9: 42 */
|
||||
channels = read_8bit (header_offset+0x05,streamHeader);
|
||||
sample_rate = read_32bitLE(header_offset+0x08,streamHeader);
|
||||
/* 0x0c (4): unk size? */
|
||||
/* 0x10 (4): ? + volume? + pan? (can be 0 for music) */
|
||||
num_samples = read_32bitLE(header_offset+0x14,streamHeader);
|
||||
/* 0x00(4): type/location? (00/01=sxd/RAM?, 02/03=sxd2/stream?) */
|
||||
codec = read_8bit (header_offset+0x04,streamHeader);
|
||||
channels = read_8bit (header_offset+0x05,streamHeader);
|
||||
sample_rate = read_32bitLE(header_offset+0x08,streamHeader);
|
||||
/* 0x0c(4): unknown size? (0x4000/0x3999/0x3333/etc, not related to extra data) */
|
||||
/* 0x10(4): ? + volume? + pan? (can be 0 for music) */
|
||||
num_samples = read_32bitLE(header_offset+0x14,streamHeader);
|
||||
loop_start_sample = read_32bitLE(header_offset+0x18,streamHeader);
|
||||
loop_end_sample = read_32bitLE(header_offset+0x1c,streamHeader);
|
||||
/* 0x20 (4): data size */
|
||||
/* 0x24 (-): extra data, variable size and format dependant
|
||||
(ATRAC9 can contain truncated part of the data, for preloading I guess) */
|
||||
/* 0x20(4): data size */
|
||||
stream_offset = read_32bitLE(header_offset+0x24,streamHeader);
|
||||
|
||||
/* Extra data, variable sized and uses some kind of TLVs (HEVAG's is optional and much smaller).
|
||||
* One tag seems to add a small part of the ATRAC9 data, for RAM preloding I guess. */
|
||||
if (codec == 0x42) {
|
||||
off_t extra_offset = header_offset+0x28;
|
||||
off_t max_offset = chunk_offset + chunk_size;
|
||||
|
||||
/* manually try to find certain tag, no idea about the actual format
|
||||
* (most variable in Soul Sacrifice; extra size isn't found in the SXD AFAIK) */
|
||||
while (extra_offset < max_offset) {
|
||||
uint32_t tag = read_32bitBE(extra_offset, streamHeader);
|
||||
if (tag == 0x0A010000 || tag == 0x0A010600) {
|
||||
at9_config_data = read_32bitLE(extra_offset+0x04,streamHeader); /* yes, LE */
|
||||
break;
|
||||
}
|
||||
|
||||
extra_offset += 0x04;
|
||||
}
|
||||
if (!at9_config_data)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
loop_flag = loop_start_sample != -1 && loop_end_sample != -1;
|
||||
|
||||
/* calc stream offset by reading stream sizes */
|
||||
stream_offset = 0;
|
||||
for (i = 0; i < total_streams; i++) {
|
||||
off_t subtable_offset, subheader_offset;
|
||||
if (i == target_stream-1) break;
|
||||
|
||||
subtable_offset = chunk_offset + 0x08 + 4*(i);
|
||||
subheader_offset = subtable_offset + read_32bitLE(subtable_offset,streamHeader);
|
||||
stream_offset += read_32bitLE(subheader_offset+0x20,streamHeader); /* data size */
|
||||
}
|
||||
|
||||
/* from current offset in sxd, absolute in sxd2 */
|
||||
if (is_separate) {
|
||||
data_offset = first_offset;
|
||||
start_offset = stream_offset;
|
||||
} else {
|
||||
if (!find_chunk_le(streamHeader, 0x44415441,first_offset,0, &data_offset,NULL)) goto fail; /* "DATA" */
|
||||
data_offset += 0x08;
|
||||
start_offset = header_offset+0x24 + stream_offset;
|
||||
}
|
||||
|
||||
start_offset = data_offset + stream_offset;
|
||||
}
|
||||
|
||||
/* get stream name (NAME is tied to REQD/cues, and SFX cues repeat WAVEs, but should work ok for streams) */
|
||||
if (is_separate && find_chunk_le(streamHeader, 0x4E414D45,first_offset,0, &chunk_offset,NULL)) { /* "NAME" */
|
||||
/* table: relative offset (32b) + hash? (32b) + cue index (32b) */
|
||||
int i;
|
||||
int num_entries = read_16bitLE(chunk_offset+0x04,streamHeader); /*can be more than streams */
|
||||
int num_entries = read_16bitLE(chunk_offset+0x04,streamHeader); /* can be bigger than streams */
|
||||
for (i = 0; i < num_entries; i++) {
|
||||
uint32_t index = (uint32_t)read_32bitLE(chunk_offset+0x08 + 0x08 + i*0x0c,streamHeader);
|
||||
if (index+1 == target_stream) {
|
||||
|
@ -113,18 +121,30 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
|
|||
if (name_offset)
|
||||
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader);
|
||||
|
||||
switch (type) {
|
||||
case 0x01: /* HEVAG */
|
||||
switch (codec) {
|
||||
case 0x21: /* HEVAG */
|
||||
vgmstream->coding_type = coding_HEVAG;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x10;
|
||||
break;
|
||||
|
||||
case 0x03: /* ATRAC9 */
|
||||
goto fail;
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case 0x42: { /* ATRAC9 */
|
||||
atrac9_config cfg = {0};
|
||||
|
||||
cfg.channels = vgmstream->channels;
|
||||
cfg.config_data = at9_config_data;
|
||||
|
||||
vgmstream->codec_data = init_atrac9(&cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_ATRAC9;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
VGM_LOG("SXD: unknown codec %i", type);
|
||||
VGM_LOG("SXD: unknown codec 0x%x", codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
|
|
@ -208,3 +208,50 @@ fail:
|
|||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Android/iOS Variants (Star Ocean Anamnesis (APK v1.9.2), Heaven x Inferno (iOS)) */
|
||||
VGMSTREAM * init_vgmstream_ta_aac_mobile(STREAMFILE *streamFile) {
|
||||
#ifdef VGM_USE_VORBIS
|
||||
off_t start_offset;
|
||||
char filename[PATH_LIMIT];
|
||||
int8_t codec_id;
|
||||
|
||||
streamFile->get_name(streamFile, filename, sizeof(filename));
|
||||
/* check extension, case insensitive */
|
||||
/* .aac: expected, .laac/ace: for players to avoid hijacking MP4/AAC */
|
||||
if (!check_extensions(streamFile, "aac,laac,ace"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitLE(0x00, streamFile) != 0x41414320) /* "AAC " */
|
||||
goto fail;
|
||||
|
||||
if (read_32bitLE(0xf0, streamFile) != 0x57415645) /* "WAVE" */
|
||||
goto fail;
|
||||
|
||||
codec_id = read_8bit(0x104, streamFile);
|
||||
if (codec_id == 0xe) /* Vorbis */
|
||||
{
|
||||
vgm_vorbis_info_t inf;
|
||||
VGMSTREAM * result = NULL;
|
||||
|
||||
memset(&inf, 0, sizeof(inf));
|
||||
inf.layout_type = layout_ogg_vorbis;
|
||||
inf.meta_type = meta_TA_AAC_VORBIS;
|
||||
inf.loop_start = read_32bitLE(0x140, streamFile);
|
||||
inf.loop_end = read_32bitLE(0x144, streamFile);
|
||||
inf.loop_flag = inf.loop_end > inf.loop_start;
|
||||
inf.loop_end_found = inf.loop_flag;
|
||||
|
||||
start_offset = read_32bitLE(0x120, streamFile);
|
||||
result = init_vgmstream_ogg_vorbis_callbacks(streamFile, filename, NULL, start_offset, &inf);
|
||||
|
||||
if (result != NULL) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
fail:
|
||||
/* clean up anything we may have opened */
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -253,7 +253,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
|||
#ifdef VGM_USE_MPEG
|
||||
case coding_MPEG_layer3:
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->codec_data = init_mpeg_codec_data(streamFile, txth.start_offset, &coding, vgmstream->channels);
|
||||
vgmstream->codec_data = init_mpeg(streamFile, txth.start_offset, &coding, vgmstream->channels);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
||||
break;
|
||||
|
|
|
@ -7,7 +7,7 @@ VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE *streamFile) {
|
|||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, off, fmt_offset;
|
||||
size_t header_size, data_size;
|
||||
int little_endian;
|
||||
int big_endian;
|
||||
int loop_flag, channel_count, block_align, bits_per_sample;
|
||||
uint32_t platform, type;
|
||||
|
||||
|
@ -28,25 +28,33 @@ VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE *streamFile) {
|
|||
else
|
||||
goto fail;
|
||||
|
||||
/* endianness is given with the platform field, but this is more versatile */
|
||||
little_endian = read_32bitBE(off+0x10,streamFile) > 0x00ffffff;
|
||||
if (little_endian) {
|
||||
read_32bit = read_32bitLE;
|
||||
read_16bit = read_16bitLE;
|
||||
} else {
|
||||
read_32bit = read_32bitBE;
|
||||
read_16bit = read_16bitBE;
|
||||
}
|
||||
|
||||
/* 0x04: version? (0x00, 0x07, 0x0a, etc); */
|
||||
platform = read_32bitBE(off+0x08,streamFile); /* string */
|
||||
type = read_32bitBE(off+0x0c,streamFile); /* string */
|
||||
|
||||
switch(platform) {
|
||||
case 0x57696920: /* "Wii " */
|
||||
case 0x43616665: /* "Cafe" */
|
||||
case 0x50533320: /* "PS3 " */
|
||||
case 0x58333630: /* "X360" */
|
||||
big_endian = 1;
|
||||
read_32bit = read_32bitBE;
|
||||
read_16bit = read_16bitBE;
|
||||
break;
|
||||
default:
|
||||
big_endian = 0;
|
||||
read_32bit = read_32bitLE;
|
||||
read_16bit = read_16bitLE;
|
||||
break;
|
||||
}
|
||||
|
||||
header_size = read_32bit(off+0x10,streamFile);
|
||||
start_offset = read_32bit(off+0x14,streamFile);
|
||||
/* 0x18: number of chunks */
|
||||
/* 0x1c: unk */
|
||||
|
||||
/* The first chunk is always "fmt" and points to a RIFF "fmt" chunk (even for WiiU or PS3) */
|
||||
/* the format has a chunk offset table, and the first one always "fmt" and points
|
||||
* to a RIFF "fmt" chunk (even for WiiU or PS3) */
|
||||
if (read_32bitBE(off+0x20,streamFile) != 0x666D7420) goto fail; /*"fmt "*/
|
||||
fmt_offset = read_32bit(off+0x24,streamFile);
|
||||
//fmt_size = read_32bit(off+0x28,streamFile);
|
||||
|
@ -74,7 +82,7 @@ VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE *streamFile) {
|
|||
/* chunks: "data" */
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = block_align; /* usually 0x04 */
|
||||
vgmstream->interleave_block_size = 0x2;
|
||||
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, bits_per_sample);
|
||||
break;
|
||||
|
@ -114,7 +122,7 @@ VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE *streamFile) {
|
|||
{
|
||||
/* get coef offsets; could check "dspL" and "dspR" chunks after "fmt " better but whatevs (only "dspL" if mono) */
|
||||
off_t dsp_coefs = read_32bitBE(off+0x30,streamFile); /* after "dspL"; spacing is consistent but could vary */
|
||||
dsp_read_coefs(vgmstream,streamFile, dsp_coefs+0x1c, 0x60, !little_endian);
|
||||
dsp_read_coefs(vgmstream,streamFile, dsp_coefs+0x1c, 0x60, big_endian);
|
||||
/* dsp_coefs + 0x00-0x1c: ? (special coefs or adpcm history?) */
|
||||
}
|
||||
|
||||
|
@ -123,8 +131,8 @@ VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE *streamFile) {
|
|||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x505333206D703320: { /* "PS3 mp3 " */
|
||||
/* chunks: "MARK" optional seek table), "STRG" (optional description), "Msf " ("data" equivalent) */
|
||||
vgmstream->codec_data = init_mpeg_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels);
|
||||
/* chunks: "MARK" (optional seek table), "STRG" (optional description), "Msf " ("data" equivalent) */
|
||||
vgmstream->codec_data = init_mpeg(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
|
@ -153,9 +161,25 @@ VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE *streamFile) {
|
|||
}
|
||||
#endif
|
||||
|
||||
case 0x5649544161743920: /*"VITAat9 "*/
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case 0x5649544161743920: { /*"VITAat9 "*/
|
||||
/* chunks: "fact" (equivalent to a RIFF "fact", num_samples + skip_samples), "data" */
|
||||
goto fail;
|
||||
atrac9_config cfg = {0};
|
||||
|
||||
cfg.channels = vgmstream->channels;
|
||||
cfg.config_data = read_32bitBE(fmt_offset+0x2c,streamFile);
|
||||
cfg.encoder_delay = read_32bit(fmt_offset+0x3c,streamFile);
|
||||
|
||||
vgmstream->codec_data = init_atrac9(&cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_ATRAC9;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
/* could get the "fact" offset but seems it always follows "fmt " */
|
||||
vgmstream->num_samples = read_32bit(fmt_offset+0x34,streamFile);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
VGM_LOG("RAKI: unknown platform %x and type %x\n", platform, type);
|
||||
|
|
|
@ -285,7 +285,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||
}
|
||||
}
|
||||
|
||||
vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg);
|
||||
vgmstream->codec_data = init_vorbis_custom(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
}
|
||||
else {
|
||||
|
@ -330,11 +330,11 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||
}
|
||||
|
||||
/* try with the selected codebooks */
|
||||
vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg);
|
||||
vgmstream->codec_data = init_vorbis_custom(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg);
|
||||
if (!vgmstream->codec_data) {
|
||||
/* codebooks failed: try again with the other type */
|
||||
cfg.setup_type = is_wem ? EXTERNAL_CODEBOOKS : AOTUV603_CODEBOOKS;
|
||||
vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg);
|
||||
vgmstream->codec_data = init_vorbis_custom(streamFile, start_offset + setup_offset, VORBIS_WWISE, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
}
|
||||
}
|
||||
|
@ -528,9 +528,24 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
|||
vgmstream->num_samples = ps_bytes_to_samples(ww.data_size, ww.channels);
|
||||
break;
|
||||
|
||||
case ATRAC9: /* PSV/PS4 */
|
||||
VGM_LOG("WWISE: ATRAC9 found (unsupported)\n");
|
||||
goto fail;
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case ATRAC9: { /* PSV/PS4 */
|
||||
atrac9_config cfg = {0};
|
||||
if (ww.extra_size != 0x12) goto fail;
|
||||
|
||||
cfg.channels = vgmstream->channels;
|
||||
cfg.config_data = read_32bitBE(ww.fmt_offset+0x18,streamFile);
|
||||
cfg.encoder_delay = read_32bit(ww.fmt_offset+0x20,streamFile);
|
||||
|
||||
vgmstream->codec_data = init_atrac9(&cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_ATRAC9;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = read_32bit(ww.fmt_offset+0x1c,streamFile);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* XNB - Microsoft XNA Game Studio 4.0 format */
|
||||
VGMSTREAM * init_vgmstream_xnb(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag = 0, channel_count;
|
||||
int flags, codec, sample_rate, block_size, bps;
|
||||
size_t xnb_size, data_size;
|
||||
|
||||
|
||||
/* check extension, case insensitive */
|
||||
if ( !check_extensions(streamFile,"xnb"))
|
||||
goto fail;
|
||||
|
||||
/* check header */
|
||||
if ((read_32bitBE(0,streamFile) & 0xFFFFFF00) != 0x584E4200) /* "XNB" */
|
||||
goto fail;
|
||||
/* 0x04: platform: ‘w’ = Microsoft Windows, ‘m’ = Windows Phone 7, ‘x’ = Xbox 360, 'a' = Android */
|
||||
|
||||
if (read_8bit(0x04,streamFile) != 0x05) /* XNA 4.0 version only */
|
||||
goto fail;
|
||||
|
||||
flags = read_8bit(0x05,streamFile);
|
||||
if (flags & 0x80) goto fail; /* compressed with XMemCompress */
|
||||
//if (flags & 0x01) goto fail; /* XMA/big endian flag? */
|
||||
|
||||
/* "check for truncated XNB" (???) */
|
||||
xnb_size = read_32bitLE(0x06,streamFile);
|
||||
if (get_streamfile_size(streamFile) < xnb_size) goto fail;
|
||||
|
||||
/* XNB contains "type reader" class references to parse "shared resource" data (can be any implemented filetype) */
|
||||
{
|
||||
char reader_name[255+1];
|
||||
off_t current_chunk = 0xa;
|
||||
int reader_string_len;
|
||||
uint32_t fmt_chunk_size;
|
||||
const char * type_sound = "Microsoft.Xna.Framework.Content.SoundEffectReader"; /* partial "fmt" chunk or XMA */
|
||||
//const char * type_song = "Microsoft.Xna.Framework.Content.SongReader"; /* just references a companion .wma */
|
||||
|
||||
/* type reader count, accept only one for now */
|
||||
if (read_8bit(current_chunk++, streamFile) != 1)
|
||||
goto fail;
|
||||
|
||||
reader_string_len = read_8bit(current_chunk++, streamFile); /* doesn't count null */
|
||||
if (reader_string_len > 255) goto fail;
|
||||
|
||||
/* check SoundEffect type string */
|
||||
if (read_string(reader_name,reader_string_len+1,current_chunk,streamFile) != reader_string_len)
|
||||
goto fail;
|
||||
if ( strcmp(reader_name, type_sound) != 0 )
|
||||
goto fail;
|
||||
current_chunk += reader_string_len + 1;
|
||||
current_chunk += 4; /* reader version */
|
||||
|
||||
/* shared resource count */
|
||||
if (read_8bit(current_chunk++, streamFile) != 1)
|
||||
goto fail;
|
||||
|
||||
/* shared resource: partial "fmt" chunk */
|
||||
fmt_chunk_size = read_32bitLE(current_chunk, streamFile);
|
||||
current_chunk += 4;
|
||||
|
||||
{
|
||||
codec = read_16bitLE(current_chunk+0x00, streamFile);
|
||||
channel_count = read_16bitLE(current_chunk+0x02, streamFile);
|
||||
sample_rate = read_32bitLE(current_chunk+0x04, streamFile);
|
||||
block_size = read_16bitLE(current_chunk+0x0c, streamFile);
|
||||
bps = read_16bitLE(current_chunk+0x0e, streamFile);
|
||||
}
|
||||
|
||||
current_chunk += fmt_chunk_size;
|
||||
|
||||
data_size = read_32bitLE(current_chunk, streamFile);
|
||||
current_chunk += 4;
|
||||
|
||||
start_offset = current_chunk;
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->meta_type = meta_XNB;
|
||||
|
||||
switch (codec) {
|
||||
case 0x01:
|
||||
vgmstream->coding_type = bps == 8 ? coding_PCM8_U_int : coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = block_size / channel_count;
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, bps);
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
vgmstream->coding_type = coding_MSADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->interleave_block_size = block_size;
|
||||
vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, block_size, channel_count);
|
||||
break;
|
||||
|
||||
case 0x11:
|
||||
vgmstream->coding_type = coding_MS_IMA;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->interleave_block_size = block_size;
|
||||
vgmstream->num_samples = ms_ima_bytes_to_samples(data_size, block_size, channel_count);
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("XNB: unknown codec 0x%x\n", codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) )
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -4,50 +4,71 @@
|
|||
|
||||
static int ps_adpcm_find_loop_offsets(STREAMFILE *streamFile, int channel_count, off_t start_offset, off_t * loop_start, off_t * loop_end);
|
||||
|
||||
/* XVAG - Sony's (second party?) format (God of War III, Ratchet & Clank Future, The Last of Us, Uncharted) */
|
||||
/* XVAG - Sony's Scream Tool/Stream Creator format (God of War III, Ratchet & Clank Future, The Last of Us, Uncharted) */
|
||||
VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
int loop_flag = 0, channel_count, codec;
|
||||
int big_endian;
|
||||
int sample_rate, num_samples, multiplier, multistreams = 0;
|
||||
int total_subsongs = 0, target_subsong = streamFile->stream_index;
|
||||
|
||||
off_t start_offset, loop_start, loop_end, chunk_offset;
|
||||
off_t first_offset = 0x20;
|
||||
int little_endian;
|
||||
int sample_rate, num_samples, multiplier;
|
||||
size_t chunk_size;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
if (!check_extensions(streamFile,"xvag")) goto fail;
|
||||
if (!check_extensions(streamFile,"xvag"))
|
||||
goto fail;
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x58564147) /* "XVAG" */
|
||||
goto fail;
|
||||
|
||||
little_endian = read_8bit(0x07,streamFile)==0; /* empty start_offset > little endian */
|
||||
if (little_endian) {
|
||||
read_32bit = read_32bitLE;
|
||||
} else {
|
||||
/* endian flag (XVAGs of the same game can use BE or LE, usually when reusing from other platforms) */
|
||||
big_endian = read_8bit(0x08,streamFile) & 0x01;
|
||||
if (big_endian) {
|
||||
read_32bit = read_32bitBE;
|
||||
} else {
|
||||
read_32bit = read_32bitLE;
|
||||
}
|
||||
|
||||
start_offset = read_32bit(0x04,streamFile);
|
||||
/* 0x08: flags? (&0x01=big endian?) 0x0a: version (chunk sizes vary) */
|
||||
/* 0x08: flags? (&0x01=big endian, 0x02=?, 0x06=full RIFF AT9?)
|
||||
* 0x09: flags2? (0x00/0x01/0x04, speaker mode?)
|
||||
* 0x0a: always 0?
|
||||
* 0x0b: version-flag? (0x5f/0x60/0x61, last has extra data) */
|
||||
|
||||
/* "fmat": base format */
|
||||
if (!find_chunk(streamFile, 0x666D6174,first_offset,0, &chunk_offset,NULL, !little_endian, 1)) goto fail; /*"fmat"*/
|
||||
/* "fmat": base format (always first) */
|
||||
if (!find_chunk(streamFile, 0x666D6174,first_offset,0, &chunk_offset,&chunk_size, big_endian, 1)) /*"fmat"*/
|
||||
goto fail;
|
||||
channel_count = read_32bit(chunk_offset+0x00,streamFile);
|
||||
codec = read_32bit(chunk_offset+0x04,streamFile);
|
||||
num_samples = read_32bit(chunk_offset+0x08,streamFile);
|
||||
/* 0x0c: samples again? */
|
||||
multiplier = read_32bit(chunk_offset+0x10,streamFile);
|
||||
/* 0x0c: samples again? playable section? */
|
||||
VGM_ASSERT(num_samples != read_32bit(chunk_offset+0x0c,streamFile), "XVAG: num_samples values don't match\n");
|
||||
|
||||
multiplier = read_32bit(chunk_offset+0x10,streamFile); /* 'interleave factor' */
|
||||
sample_rate = read_32bit(chunk_offset+0x14,streamFile);
|
||||
/* 0x18: datasize */
|
||||
|
||||
/* extra data, seen in MPEG/ATRAC9 */
|
||||
if (chunk_size > 0x1c) {
|
||||
total_subsongs = read_32bit(chunk_offset+0x1c,streamFile); /* number of interleaved layers */
|
||||
multistreams = read_32bit(chunk_offset+0x20,streamFile); /* number of bitstreams per layer (for multistream Nch MPEG/ATRAC9) */
|
||||
}
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
|
||||
|
||||
/* other chunks: */
|
||||
/* "cpan": pan/volume per channel */
|
||||
/* "cues": cue/labels (rare) */
|
||||
/* "0000": end chunk before start_offset */
|
||||
|
||||
//if ((uint16_t)read_16bitBE(start_offset,streamFile)==0xFFFB) codec = 0x08;
|
||||
if (codec == 0x06) { /* todo not sure if there are any looping XVAGs */
|
||||
/* some XVAG seem to do full loops, this should detect them as looping */
|
||||
//todo remove, looping seems external and specified in Scream Tool's bank formats
|
||||
if (codec == 0x06) {
|
||||
loop_flag = ps_adpcm_find_loop_offsets(streamFile, channel_count, start_offset, &loop_start, &loop_end);
|
||||
}
|
||||
|
||||
|
@ -57,52 +78,85 @@ VGMSTREAM * init_vgmstream_xvag(STREAMFILE *streamFile) {
|
|||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->meta_type = meta_XVAG;
|
||||
|
||||
switch (codec) {
|
||||
case 0x06: /* PS ADPCM: God of War III, Uncharted 1/2, Ratchet and Clank Future */
|
||||
case 0x07: { /* Bizarro 6ch PS ADPCM: infamous 1 (todo won't play properly; algo tweak + bigger predictor table?) */
|
||||
//case 0x??: /* PCM? */
|
||||
case 0x06: /* VAG (PS ADPCM): God of War III (PS3), Uncharted 1/2 (PS3), Ratchet and Clank Future (PS3) */
|
||||
case 0x07: /* SVAG? (PS ADPCM with extended table): inFamous 1 (PS3) */
|
||||
if (total_subsongs > 1 || multistreams > 1) goto fail;
|
||||
if (multiplier > 1) goto fail;
|
||||
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x10;//* multiplier? (doesn't seem necessary, always 1);
|
||||
vgmstream->interleave_block_size = 0x10;
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
|
||||
if (loop_flag) {
|
||||
if (loop_start!=0) {
|
||||
vgmstream->loop_start_sample = ((((loop_start/vgmstream->interleave_block_size)-1)*vgmstream->interleave_block_size)/16*28)/channel_count;
|
||||
if(loop_start%vgmstream->interleave_block_size)
|
||||
vgmstream->loop_start_sample += (((loop_start%vgmstream->interleave_block_size)-1)/16*14*channel_count);
|
||||
}
|
||||
vgmstream->loop_end_sample = ((((loop_end/vgmstream->interleave_block_size)-1)*vgmstream->interleave_block_size)/16*28)/channel_count;
|
||||
if (loop_end%vgmstream->interleave_block_size)
|
||||
vgmstream->loop_end_sample += (((loop_end%vgmstream->interleave_block_size)-1)/16*14*channel_count);
|
||||
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, vgmstream->channels);
|
||||
vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, vgmstream->channels);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x08: { /* MPEG: The Last of Us, Uncharted 3, Medieval Moves */
|
||||
case 0x08: { /* MPEG: The Last of Us (PS3), Uncharted 3 (PS3), Medieval Moves (PS3) */
|
||||
mpeg_custom_config cfg = {0};
|
||||
|
||||
if (total_subsongs > 1 || (multistreams > 1 && multistreams == vgmstream->channels)) goto fail;
|
||||
|
||||
/* "mpin": mpeg info */
|
||||
/* 0x00/04: mpeg version/layer? other: unknown or repeats of "fmat" */
|
||||
if (!find_chunk(streamFile, 0x6D70696E,first_offset,0, &chunk_offset,NULL, !little_endian, 1))
|
||||
goto fail; /*"mpin"*/
|
||||
if (!find_chunk(streamFile, 0x6D70696E,first_offset,0, &chunk_offset,NULL, big_endian, 1)) /*"mpin"*/
|
||||
goto fail;
|
||||
|
||||
cfg.chunk_size = read_32bit(chunk_offset+0x1c,streamFile); /* fixed frame size */
|
||||
cfg.interleave = cfg.chunk_size * multiplier;
|
||||
|
||||
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_XVAG, &cfg);
|
||||
vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_XVAG, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case 0x09: { /* ATRAC9: Sly Cooper and the Thievius Raccoonus */
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case 0x09: { /* ATRAC9: Sly Cooper and the Thievius Raccoonus (Vita), The Last of Us Remastered (PS4) */
|
||||
atrac9_config cfg = {0};
|
||||
|
||||
/* "a9in": ATRAC9 info */
|
||||
goto fail;
|
||||
/* 0x00: block align, 0x04: samples per block, 0x0c: fact num_samples (no change), 0x10: encoder delay1 */
|
||||
if (!find_chunk(streamFile, 0x6139696E,first_offset,0, &chunk_offset,NULL, big_endian, 1)) /*"a9in"*/
|
||||
goto fail;
|
||||
|
||||
cfg.type = ATRAC9_XVAG;
|
||||
cfg.channels = vgmstream->channels;
|
||||
cfg.config_data = read_32bitBE(chunk_offset+0x08,streamFile);
|
||||
cfg.encoder_delay = read_32bit(chunk_offset+0x14,streamFile);
|
||||
|
||||
if (total_subsongs > 1 && multistreams > 1) {
|
||||
goto fail; /* not known */
|
||||
}
|
||||
else if (total_subsongs > 1) {
|
||||
/* interleaves 'multiplier' superframes per subsong (all share config_data) */
|
||||
cfg.interleave_skip = read_32bit(chunk_offset+0x00,streamFile) * multiplier;
|
||||
cfg.subsong_skip = total_subsongs;
|
||||
/* start in subsong's first superframe */
|
||||
start_offset += (target_subsong-1) * cfg.interleave_skip * (cfg.subsong_skip-1);
|
||||
}
|
||||
else if (multistreams > 1) {
|
||||
/* Vita multichannel (flower) interleaves streams like MPEG
|
||||
* PS4 (The Last of Us) uses ATRAC9's multichannel directly instead (multistreams==1) */
|
||||
goto fail;//todo add
|
||||
}
|
||||
//if (multistreams == vgmstream->channels) goto fail;
|
||||
|
||||
vgmstream->codec_data = init_atrac9(&cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_ATRAC9;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
|
|
|
@ -17,10 +17,10 @@ static void try_dual_file_stereo(VGMSTREAM * opened_vgmstream, STREAMFILE *strea
|
|||
VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_adx,
|
||||
init_vgmstream_brstm,
|
||||
init_vgmstream_bfwav,
|
||||
init_vgmstream_bfstm,
|
||||
init_vgmstream_mca,
|
||||
init_vgmstream_btsnd,
|
||||
init_vgmstream_bfwav,
|
||||
init_vgmstream_bfstm,
|
||||
init_vgmstream_mca,
|
||||
init_vgmstream_btsnd,
|
||||
init_vgmstream_nds_strm,
|
||||
init_vgmstream_agsc,
|
||||
init_vgmstream_ngc_adpdtk,
|
||||
|
@ -31,7 +31,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
|||
init_vgmstream_rs03,
|
||||
init_vgmstream_ngc_dsp_std,
|
||||
init_vgmstream_ngc_mdsp_std,
|
||||
init_vgmstream_ngc_dsp_csmp,
|
||||
init_vgmstream_ngc_dsp_csmp,
|
||||
init_vgmstream_cstr,
|
||||
init_vgmstream_gcsw,
|
||||
init_vgmstream_ps2_ads,
|
||||
|
@ -67,10 +67,10 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
|||
init_vgmstream_sfl,
|
||||
#endif
|
||||
#if 0
|
||||
init_vgmstream_mp4_aac,
|
||||
init_vgmstream_mp4_aac,
|
||||
#endif
|
||||
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
|
||||
init_vgmstream_akb,
|
||||
init_vgmstream_akb,
|
||||
#endif
|
||||
init_vgmstream_sadb,
|
||||
init_vgmstream_ps2_bmdx,
|
||||
|
@ -117,7 +117,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
|||
init_vgmstream_ps2_dxh,
|
||||
init_vgmstream_ps2_psh,
|
||||
init_vgmstream_scd_pcm,
|
||||
init_vgmstream_ps2_pcm,
|
||||
init_vgmstream_ps2_pcm,
|
||||
init_vgmstream_ps2_rkv,
|
||||
init_vgmstream_ps2_psw,
|
||||
init_vgmstream_ps2_vas,
|
||||
|
@ -162,13 +162,13 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
|||
init_vgmstream_rsd2vag,
|
||||
init_vgmstream_rsd2pcmb,
|
||||
init_vgmstream_rsd2xadp,
|
||||
init_vgmstream_rsd3vag,
|
||||
init_vgmstream_rsd3gadp,
|
||||
init_vgmstream_rsd3vag,
|
||||
init_vgmstream_rsd3gadp,
|
||||
init_vgmstream_rsd3pcm,
|
||||
init_vgmstream_rsd3pcmb,
|
||||
init_vgmstream_rsd3pcmb,
|
||||
init_vgmstream_rsd4pcmb,
|
||||
init_vgmstream_rsd4pcm,
|
||||
init_vgmstream_rsd4radp,
|
||||
init_vgmstream_rsd4radp,
|
||||
init_vgmstream_rsd4vag,
|
||||
init_vgmstream_rsd6vag,
|
||||
init_vgmstream_rsd6wadp,
|
||||
|
@ -222,19 +222,19 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
|||
init_vgmstream_ps2_sps,
|
||||
init_vgmstream_ps2_xa2_rrp,
|
||||
init_vgmstream_nds_hwas,
|
||||
init_vgmstream_ngc_lps,
|
||||
init_vgmstream_ngc_lps,
|
||||
init_vgmstream_ps2_snd,
|
||||
init_vgmstream_naomi_adpcm,
|
||||
init_vgmstream_sd9,
|
||||
init_vgmstream_2dx9,
|
||||
init_vgmstream_dsp_ygo,
|
||||
init_vgmstream_sd9,
|
||||
init_vgmstream_2dx9,
|
||||
init_vgmstream_dsp_ygo,
|
||||
init_vgmstream_ps2_vgv,
|
||||
init_vgmstream_ngc_gcub,
|
||||
init_vgmstream_maxis_xa,
|
||||
init_vgmstream_ngc_sck_dsp,
|
||||
init_vgmstream_apple_caff,
|
||||
init_vgmstream_pc_mxst,
|
||||
init_vgmstream_sab,
|
||||
init_vgmstream_pc_mxst,
|
||||
init_vgmstream_sab,
|
||||
init_vgmstream_exakt_sc,
|
||||
init_vgmstream_wii_bns,
|
||||
init_vgmstream_wii_was,
|
||||
|
@ -244,8 +244,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
|||
init_vgmstream_stx,
|
||||
init_vgmstream_myspd,
|
||||
init_vgmstream_his,
|
||||
init_vgmstream_ps2_ast,
|
||||
init_vgmstream_dmsg,
|
||||
init_vgmstream_ps2_ast,
|
||||
init_vgmstream_dmsg,
|
||||
init_vgmstream_ngc_dsp_aaap,
|
||||
init_vgmstream_ngc_dsp_konami,
|
||||
init_vgmstream_ps2_ster,
|
||||
|
@ -261,8 +261,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
|||
init_vgmstream_ngc_bo2,
|
||||
init_vgmstream_dsp_ddsp,
|
||||
init_vgmstream_p3d,
|
||||
init_vgmstream_ps2_tk1,
|
||||
init_vgmstream_ps2_adsc,
|
||||
init_vgmstream_ps2_tk1,
|
||||
init_vgmstream_ps2_adsc,
|
||||
init_vgmstream_ngc_dsp_mpds,
|
||||
init_vgmstream_dsp_str_ig,
|
||||
init_vgmstream_psx_mgav,
|
||||
|
@ -274,57 +274,58 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
|||
init_vgmstream_dsp_xiii,
|
||||
init_vgmstream_dsp_cabelas,
|
||||
init_vgmstream_ps2_adm,
|
||||
init_vgmstream_ps2_lpcm,
|
||||
init_vgmstream_ps2_lpcm,
|
||||
init_vgmstream_dsp_bdsp,
|
||||
init_vgmstream_ps2_vms,
|
||||
init_vgmstream_xau,
|
||||
init_vgmstream_ps2_vms,
|
||||
init_vgmstream_xau,
|
||||
init_vgmstream_bar,
|
||||
init_vgmstream_ffw,
|
||||
init_vgmstream_dsp_dspw,
|
||||
init_vgmstream_ps2_jstm,
|
||||
init_vgmstream_xvag,
|
||||
init_vgmstream_ps3_cps,
|
||||
init_vgmstream_ps3_cps,
|
||||
init_vgmstream_sqex_scd,
|
||||
init_vgmstream_ngc_nst_dsp,
|
||||
init_vgmstream_baf,
|
||||
init_vgmstream_ps3_msf,
|
||||
init_vgmstream_nub_vag,
|
||||
init_vgmstream_ps3_past,
|
||||
init_vgmstream_nub_vag,
|
||||
init_vgmstream_ps3_past,
|
||||
init_vgmstream_sgxd,
|
||||
init_vgmstream_ngca,
|
||||
init_vgmstream_wii_ras,
|
||||
init_vgmstream_ps2_spm,
|
||||
init_vgmstream_x360_tra,
|
||||
init_vgmstream_ps2_iab,
|
||||
init_vgmstream_ps2_strlr,
|
||||
init_vgmstream_ngca,
|
||||
init_vgmstream_wii_ras,
|
||||
init_vgmstream_ps2_spm,
|
||||
init_vgmstream_x360_tra,
|
||||
init_vgmstream_ps2_iab,
|
||||
init_vgmstream_ps2_strlr,
|
||||
init_vgmstream_lsf_n1nj4n,
|
||||
init_vgmstream_vawx,
|
||||
init_vgmstream_vawx,
|
||||
init_vgmstream_pc_snds,
|
||||
init_vgmstream_ps2_wmus,
|
||||
init_vgmstream_hyperscan_kvag,
|
||||
init_vgmstream_ios_psnd,
|
||||
init_vgmstream_pc_adp_bos,
|
||||
init_vgmstream_pc_adp_otns,
|
||||
init_vgmstream_ps2_wmus,
|
||||
init_vgmstream_hyperscan_kvag,
|
||||
init_vgmstream_ios_psnd,
|
||||
init_vgmstream_pc_adp_bos,
|
||||
init_vgmstream_pc_adp_otns,
|
||||
init_vgmstream_eb_sfx,
|
||||
init_vgmstream_eb_sf0,
|
||||
init_vgmstream_ps3_klbs,
|
||||
init_vgmstream_ps3_klbs,
|
||||
init_vgmstream_ps2_mtaf,
|
||||
init_vgmstream_tun,
|
||||
init_vgmstream_wpd,
|
||||
init_vgmstream_mn_str,
|
||||
init_vgmstream_mss,
|
||||
init_vgmstream_ps2_hsf,
|
||||
init_vgmstream_ps3_ivag,
|
||||
init_vgmstream_ps2_2pfs,
|
||||
init_vgmstream_xnbm,
|
||||
init_vgmstream_rsd6oogv,
|
||||
init_vgmstream_ubi_ckd,
|
||||
init_vgmstream_ps2_vbk,
|
||||
init_vgmstream_otm,
|
||||
init_vgmstream_bcstm,
|
||||
init_vgmstream_3ds_idsp,
|
||||
init_vgmstream_tun,
|
||||
init_vgmstream_wpd,
|
||||
init_vgmstream_mn_str,
|
||||
init_vgmstream_mss,
|
||||
init_vgmstream_ps2_hsf,
|
||||
init_vgmstream_ps3_ivag,
|
||||
init_vgmstream_ps2_2pfs,
|
||||
init_vgmstream_xnb,
|
||||
init_vgmstream_rsd6oogv,
|
||||
init_vgmstream_ubi_ckd,
|
||||
init_vgmstream_ps2_vbk,
|
||||
init_vgmstream_otm,
|
||||
init_vgmstream_bcstm,
|
||||
init_vgmstream_3ds_idsp,
|
||||
init_vgmstream_kt_g1l,
|
||||
init_vgmstream_kt_wiibgm,
|
||||
init_vgmstream_ktss,
|
||||
init_vgmstream_hca,
|
||||
init_vgmstream_ps2_svag_snk,
|
||||
init_vgmstream_ps2_vds_vdm,
|
||||
|
@ -349,6 +350,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
|||
init_vgmstream_rsd6xma,
|
||||
init_vgmstream_ta_aac_x360,
|
||||
init_vgmstream_ta_aac_ps3,
|
||||
init_vgmstream_ta_aac_mobile,
|
||||
init_vgmstream_ps3_mta2,
|
||||
init_vgmstream_ngc_ulw,
|
||||
init_vgmstream_pc_xa30,
|
||||
|
@ -370,7 +372,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
|||
init_vgmstream_ea_sps,
|
||||
init_vgmstream_ngc_vid1,
|
||||
init_vgmstream_flx,
|
||||
init_vgmstream_mogg,
|
||||
init_vgmstream_mogg,
|
||||
|
||||
init_vgmstream_txth, /* should go at the end (lower priority) */
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
|
@ -512,9 +514,9 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
|
|||
}
|
||||
|
||||
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
|
||||
if (vgmstream->coding_type==coding_MP4_AAC) {
|
||||
reset_mp4_aac(vgmstream);
|
||||
}
|
||||
if (vgmstream->coding_type==coding_MP4_AAC) {
|
||||
reset_mp4_aac(vgmstream);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
|
@ -541,11 +543,17 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
|
|||
#endif
|
||||
|
||||
#ifdef VGM_USE_MAIATRAC3PLUS
|
||||
if (vgmstream->coding_type==coding_AT3plus) {
|
||||
reset_at3plus(vgmstream);
|
||||
}
|
||||
if (vgmstream->coding_type==coding_AT3plus) {
|
||||
reset_at3plus(vgmstream);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
if (vgmstream->coding_type==coding_ATRAC9) {
|
||||
reset_atrac9(vgmstream);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
if (vgmstream->coding_type==coding_FFmpeg) {
|
||||
reset_ffmpeg(vgmstream);
|
||||
|
@ -701,10 +709,10 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
|
|||
#endif
|
||||
|
||||
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
|
||||
if (vgmstream->coding_type==coding_MP4_AAC) {
|
||||
free_mp4_aac(vgmstream->codec_data);
|
||||
if (vgmstream->coding_type==coding_MP4_AAC) {
|
||||
free_mp4_aac(vgmstream->codec_data);
|
||||
vgmstream->codec_data = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
|
@ -734,10 +742,17 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
|
|||
#endif
|
||||
|
||||
#ifdef VGM_USE_MAIATRAC3PLUS
|
||||
if (vgmstream->coding_type == coding_AT3plus) {
|
||||
free_at3plus(vgmstream->codec_data);
|
||||
if (vgmstream->coding_type == coding_AT3plus) {
|
||||
free_at3plus(vgmstream->codec_data);
|
||||
vgmstream->codec_data = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
if (vgmstream->coding_type == coding_ATRAC9) {
|
||||
free_atrac9(vgmstream->codec_data);
|
||||
vgmstream->codec_data = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (vgmstream->coding_type==coding_ACM) {
|
||||
|
@ -926,7 +941,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre
|
|||
case layout_none:
|
||||
render_vgmstream_nolayout(buffer,sample_count,vgmstream);
|
||||
break;
|
||||
case layout_mxch_blocked:
|
||||
case layout_mxch_blocked:
|
||||
case layout_ast_blocked:
|
||||
case layout_halpst_blocked:
|
||||
case layout_xa_blocked:
|
||||
|
@ -949,8 +964,8 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre
|
|||
case layout_psx_mgav_blocked:
|
||||
case layout_ps2_adm_blocked:
|
||||
case layout_dsp_bdsp_blocked:
|
||||
case layout_tra_blocked:
|
||||
case layout_ps2_iab_blocked:
|
||||
case layout_tra_blocked:
|
||||
case layout_ps2_iab_blocked:
|
||||
case layout_ps2_strlr_blocked:
|
||||
case layout_rws_blocked:
|
||||
case layout_hwas_blocked:
|
||||
|
@ -986,7 +1001,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
|||
case coding_CRI_ADX_exp:
|
||||
case coding_CRI_ADX_enc_8:
|
||||
case coding_CRI_ADX_enc_9:
|
||||
return (vgmstream->interleave_block_size - 2) * 2;
|
||||
return (vgmstream->interleave_block_size - 2) * 2;
|
||||
case coding_L5_555:
|
||||
return 32;
|
||||
case coding_NGC_DSP:
|
||||
|
@ -1002,6 +1017,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
|||
case coding_PCM8_SB_int:
|
||||
case coding_PCM8_U_int:
|
||||
case coding_ULAW:
|
||||
case coding_ULAW_int:
|
||||
case coding_ALAW:
|
||||
case coding_PCMFLOAT:
|
||||
return 1;
|
||||
|
@ -1056,14 +1072,14 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
|||
case coding_PSX_cfg:
|
||||
return (vgmstream->interleave_block_size - 1) * 2; /* decodes 1 byte into 2 bytes */
|
||||
case coding_XBOX:
|
||||
case coding_XBOX_int:
|
||||
case coding_XBOX_int:
|
||||
case coding_FSB_IMA:
|
||||
return 64;
|
||||
case coding_EA_XA:
|
||||
case coding_EA_XA_int:
|
||||
case coding_EA_XA_V2:
|
||||
case coding_MAXIS_XA:
|
||||
return 28;
|
||||
return 28;
|
||||
case coding_EA_XAS:
|
||||
return 128;
|
||||
case coding_WS:
|
||||
|
@ -1099,7 +1115,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
|||
{
|
||||
ffmpeg_codec_data *data = (ffmpeg_codec_data *) vgmstream->codec_data;
|
||||
if (data) {
|
||||
/* must know the full block size for edge loops */
|
||||
/* must know the full block size for edge loops */
|
||||
return data->sampleBufferBlock;
|
||||
}
|
||||
return 0;
|
||||
|
@ -1119,12 +1135,16 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
|||
case coding_CRI_HCA:
|
||||
return clHCA_samplesPerBlock;
|
||||
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
|
||||
case coding_MP4_AAC:
|
||||
return ((mp4_aac_codec_data*)vgmstream->codec_data)->samples_per_frame;
|
||||
case coding_MP4_AAC:
|
||||
return ((mp4_aac_codec_data*)vgmstream->codec_data)->samples_per_frame;
|
||||
#endif
|
||||
#ifdef VGM_USE_MAIATRAC3PLUS
|
||||
case coding_AT3plus:
|
||||
return 2048 - ((maiatrac3plus_codec_data*)vgmstream->codec_data)->samples_discard;
|
||||
case coding_AT3plus:
|
||||
return 2048 - ((maiatrac3plus_codec_data*)vgmstream->codec_data)->samples_discard;
|
||||
#endif
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case coding_ATRAC9:
|
||||
return 0; /* varies with config data, usually 256 or 1024 */
|
||||
#endif
|
||||
default:
|
||||
return 0;
|
||||
|
@ -1157,6 +1177,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
|
|||
case coding_PCM8_SB_int:
|
||||
case coding_PCM8_U_int:
|
||||
case coding_ULAW:
|
||||
case coding_ULAW_int:
|
||||
case coding_ALAW:
|
||||
return 1;
|
||||
case coding_PCMFLOAT:
|
||||
|
@ -1204,7 +1225,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
|
|||
case coding_XA:
|
||||
return 14*vgmstream->channels;
|
||||
case coding_XBOX:
|
||||
case coding_XBOX_int:
|
||||
case coding_XBOX_int:
|
||||
case coding_FSB_IMA:
|
||||
return 36;
|
||||
case coding_EA_XA:
|
||||
|
@ -1239,7 +1260,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
|
|||
case coding_G719:
|
||||
#endif
|
||||
#ifdef VGM_USE_MAIATRAC3PLUS
|
||||
case coding_AT3plus:
|
||||
case coding_AT3plus:
|
||||
#endif
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case coding_FFmpeg:
|
||||
|
@ -1253,6 +1274,10 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
|
|||
return 0x04;
|
||||
case coding_EA_MT:
|
||||
return 0; /* variable (frames of bit counts or PCM frames) */
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case coding_ATRAC9:
|
||||
return 0; /* varies with config data, usually 0x100-200 */
|
||||
#endif
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
@ -1405,6 +1430,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
|||
samples_to_do);
|
||||
}
|
||||
break;
|
||||
case coding_ULAW_int:
|
||||
for (chan=0;chan<vgmstream->channels;chan++) {
|
||||
decode_ulaw_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
|
||||
vgmstream->channels,vgmstream->samples_into_block,
|
||||
samples_to_do);
|
||||
}
|
||||
break;
|
||||
case coding_ALAW:
|
||||
for (chan=0;chan<vgmstream->channels;chan++) {
|
||||
decode_alaw(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
|
||||
|
@ -1595,11 +1627,11 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
|||
break;
|
||||
#endif
|
||||
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
|
||||
case coding_MP4_AAC:
|
||||
decode_mp4_aac(vgmstream->codec_data,
|
||||
buffer+samples_written*vgmstream->channels,samples_to_do,
|
||||
vgmstream->channels);
|
||||
break;
|
||||
case coding_MP4_AAC:
|
||||
decode_mp4_aac(vgmstream->codec_data,
|
||||
buffer+samples_written*vgmstream->channels,samples_to_do,
|
||||
vgmstream->channels);
|
||||
break;
|
||||
#endif
|
||||
case coding_SDX2:
|
||||
for (chan=0;chan<vgmstream->channels;chan++) {
|
||||
|
@ -1752,15 +1784,23 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
|||
break;
|
||||
#endif
|
||||
#ifdef VGM_USE_MAIATRAC3PLUS
|
||||
case coding_AT3plus:
|
||||
for (chan=0;chan<vgmstream->channels;chan++) {
|
||||
decode_at3plus(vgmstream,
|
||||
buffer+samples_written*vgmstream->channels+chan,
|
||||
vgmstream->channels,
|
||||
samples_to_do,
|
||||
chan);
|
||||
}
|
||||
break;
|
||||
case coding_AT3plus:
|
||||
for (chan=0;chan<vgmstream->channels;chan++) {
|
||||
decode_at3plus(vgmstream,
|
||||
buffer+samples_written*vgmstream->channels+chan,
|
||||
vgmstream->channels,
|
||||
samples_to_do,
|
||||
chan);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case coding_ATRAC9:
|
||||
decode_atrac9(vgmstream,
|
||||
buffer+samples_written*vgmstream->channels,
|
||||
samples_to_do,
|
||||
vgmstream->channels);
|
||||
break;
|
||||
#endif
|
||||
case coding_ACM:
|
||||
/* handled in its own layout, here to quiet compiler */
|
||||
|
@ -1964,6 +2004,12 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
if (vgmstream->coding_type==coding_ATRAC9) {
|
||||
seek_atrac9(vgmstream, vgmstream->loop_sample);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
if (vgmstream->coding_type==coding_MPEG_custom ||
|
||||
vgmstream->coding_type==coding_MPEG_ealayer3 ||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue