Updated VGMStream to r1050-925-g2b92a562, now with Atrac9 support.

CQTexperiment
Christopher Snowhill 2018-01-08 18:54:28 -08:00
parent 5ca94b626d
commit ddc3da7576
101 changed files with 10387 additions and 1250 deletions

View File

@ -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 */;
}

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}
}
}

View File

@ -0,0 +1,6 @@
#pragma once
#include "unpack.h"
at9_status CreateGradient(block* block);
void CalculateMask(channel* channel);
void CalculatePrecisions(channel* channel);

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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)

View File

@ -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

View File

@ -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]];
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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]];
}
}
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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];

View File

@ -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];

View File

@ -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);
}
}
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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");
}
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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
};
}
}

View File

@ -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;
}
}
}

View File

@ -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
}
}

View File

@ -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;
}
}

View File

@ -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; }
}
}

View File

@ -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);
}
}
}
}

View File

@ -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

View File

@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
</Project>

View File

@ -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]];
}
}
}
}
}

View File

@ -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
}
};
}
}

View File

@ -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];
}
}
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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
}
}
}

View File

@ -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
};
}
}

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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];
}

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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) {

View File

@ -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;

View File

@ -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);;
}
}

View File

@ -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

View File

@ -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;

View File

@ -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]);

View File

@ -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"},

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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 */
}

View File

@ -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: /* ? */

View File

@ -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 */

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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_*/

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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