Changed more bundle identifiers; Added NCSF player to HighlyComplete, which necessitates using libc++, forcing this plugin to require 10.7

CQTexperiment
Chris Moeller 2013-10-10 17:13:18 -07:00
parent dda7bf994d
commit cee6ab4b8f
54 changed files with 5178 additions and 32 deletions

View File

@ -328,7 +328,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.6;
MACOSX_DEPLOYMENT_TARGET = 10.7;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
@ -360,7 +360,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.6;
MACOSX_DEPLOYMENT_TARGET = 10.7;
SDKROOT = macosx;
};
name = Release;

View File

@ -9,7 +9,7 @@
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>NoWork-Inc.${PRODUCT_NAME:rfc1034identifier}</string>
<string>org.nowork.highlyadvanced</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>

View File

@ -265,7 +265,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.6;
MACOSX_DEPLOYMENT_TARGET = 10.7;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
@ -297,7 +297,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.6;
MACOSX_DEPLOYMENT_TARGET = 10.7;
SDKROOT = macosx;
};
name = Release;
@ -320,7 +320,6 @@
);
INFOPLIST_FILE = "HighlyExperimental/HighlyExperimental-Info.plist";
INSTALL_PATH = "@loader_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.6;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
WRAPPER_EXTENSION = framework;
@ -344,7 +343,6 @@
);
INFOPLIST_FILE = "HighlyExperimental/HighlyExperimental-Info.plist";
INSTALL_PATH = "@loader_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.6;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
WRAPPER_EXTENSION = framework;

View File

@ -9,7 +9,7 @@
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>NoWork-Inc.${PRODUCT_NAME:rfc1034identifier}</string>
<string>org.nowork.highlyexperimental</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>

View File

@ -235,7 +235,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.6;
MACOSX_DEPLOYMENT_TARGET = 10.7;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
SKIP_INSTALL = YES;
@ -273,7 +273,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.6;
MACOSX_DEPLOYMENT_TARGET = 10.7;
SDKROOT = macosx;
SKIP_INSTALL = YES;
};

View File

@ -9,7 +9,7 @@
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>NoWork-Inc.${PRODUCT_NAME:rfc1034identifier}</string>
<string>org.nowork.highlyquixotic</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>

View File

@ -283,7 +283,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.6;
MACOSX_DEPLOYMENT_TARGET = 10.7;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
@ -323,7 +323,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.6;
MACOSX_DEPLOYMENT_TARGET = 10.7;
SDKROOT = macosx;
};
name = Release;

View File

@ -9,7 +9,7 @@
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>NoWork-Inc.${PRODUCT_NAME:rfc1034identifier}</string>
<string>org.nowork.highlytheoretical</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>

View File

@ -0,0 +1,446 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
83848FBC1807623F00E7332D /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83848FBB1807623F00E7332D /* Cocoa.framework */; };
83848FC61807623F00E7332D /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 83848FC41807623F00E7332D /* InfoPlist.strings */; };
838490241807649A00E7332D /* BigSString.h in Headers */ = {isa = PBXBuildFile; fileRef = 838490001807649A00E7332D /* BigSString.h */; };
838490251807649A00E7332D /* Channel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 838490011807649A00E7332D /* Channel.cpp */; };
838490261807649A00E7332D /* Channel.h in Headers */ = {isa = PBXBuildFile; fileRef = 838490021807649A00E7332D /* Channel.h */; settings = {ATTRIBUTES = (Public, ); }; };
838490271807649A00E7332D /* common.h in Headers */ = {isa = PBXBuildFile; fileRef = 838490031807649A00E7332D /* common.h */; settings = {ATTRIBUTES = (Public, ); }; };
838490281807649A00E7332D /* consts.h in Headers */ = {isa = PBXBuildFile; fileRef = 838490041807649A00E7332D /* consts.h */; settings = {ATTRIBUTES = (Public, ); }; };
838490291807649A00E7332D /* convert.h in Headers */ = {isa = PBXBuildFile; fileRef = 838490051807649A00E7332D /* convert.h */; };
8384902A1807649A00E7332D /* ConvertUTF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 838490061807649A00E7332D /* ConvertUTF.cpp */; };
8384902B1807649A00E7332D /* ConvertUTF.h in Headers */ = {isa = PBXBuildFile; fileRef = 838490071807649A00E7332D /* ConvertUTF.h */; };
8384902C1807649A00E7332D /* FATSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 838490081807649A00E7332D /* FATSection.cpp */; };
8384902D1807649A00E7332D /* FATSection.h in Headers */ = {isa = PBXBuildFile; fileRef = 838490091807649A00E7332D /* FATSection.h */; };
8384902E1807649A00E7332D /* INFOEntry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8384900A1807649A00E7332D /* INFOEntry.cpp */; };
8384902F1807649A00E7332D /* INFOEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = 8384900B1807649A00E7332D /* INFOEntry.h */; settings = {ATTRIBUTES = (Public, ); }; };
838490301807649A00E7332D /* INFOSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8384900C1807649A00E7332D /* INFOSection.cpp */; };
838490311807649A00E7332D /* INFOSection.h in Headers */ = {isa = PBXBuildFile; fileRef = 8384900D1807649A00E7332D /* INFOSection.h */; };
838490321807649A00E7332D /* NDSStdHeader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8384900E1807649A00E7332D /* NDSStdHeader.cpp */; };
838490331807649A00E7332D /* NDSStdHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 8384900F1807649A00E7332D /* NDSStdHeader.h */; };
838490341807649A00E7332D /* Player.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 838490101807649A00E7332D /* Player.cpp */; };
838490351807649A00E7332D /* Player.h in Headers */ = {isa = PBXBuildFile; fileRef = 838490111807649A00E7332D /* Player.h */; settings = {ATTRIBUTES = (Public, ); }; };
838490361807649A00E7332D /* SBNK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 838490121807649A00E7332D /* SBNK.cpp */; };
838490371807649A00E7332D /* SBNK.h in Headers */ = {isa = PBXBuildFile; fileRef = 838490131807649A00E7332D /* SBNK.h */; settings = {ATTRIBUTES = (Public, ); }; };
838490381807649A00E7332D /* SDAT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 838490141807649A00E7332D /* SDAT.cpp */; };
838490391807649A00E7332D /* SDAT.h in Headers */ = {isa = PBXBuildFile; fileRef = 838490151807649A00E7332D /* SDAT.h */; settings = {ATTRIBUTES = (Public, ); }; };
8384903A1807649A00E7332D /* SSEQ.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 838490161807649A00E7332D /* SSEQ.cpp */; };
8384903B1807649A00E7332D /* SSEQ.h in Headers */ = {isa = PBXBuildFile; fileRef = 838490171807649A00E7332D /* SSEQ.h */; settings = {ATTRIBUTES = (Public, ); }; };
8384903C1807649A00E7332D /* SWAR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 838490181807649A00E7332D /* SWAR.cpp */; };
8384903D1807649A00E7332D /* SWAR.h in Headers */ = {isa = PBXBuildFile; fileRef = 838490191807649A00E7332D /* SWAR.h */; settings = {ATTRIBUTES = (Public, ); }; };
8384903E1807649A00E7332D /* SWAV.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8384901A1807649A00E7332D /* SWAV.cpp */; };
8384903F1807649A00E7332D /* SWAV.h in Headers */ = {isa = PBXBuildFile; fileRef = 8384901B1807649A00E7332D /* SWAV.h */; settings = {ATTRIBUTES = (Public, ); }; };
838490401807649A00E7332D /* SYMBSection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8384901C1807649A00E7332D /* SYMBSection.cpp */; };
838490411807649A00E7332D /* SYMBSection.h in Headers */ = {isa = PBXBuildFile; fileRef = 8384901D1807649A00E7332D /* SYMBSection.h */; };
838490421807649A00E7332D /* Track.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8384901E1807649A00E7332D /* Track.cpp */; };
838490431807649A00E7332D /* Track.h in Headers */ = {isa = PBXBuildFile; fileRef = 8384901F1807649A00E7332D /* Track.h */; settings = {ATTRIBUTES = (Public, ); }; };
838490441807649A00E7332D /* UtfConverter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 838490201807649A00E7332D /* UtfConverter.cpp */; };
838490451807649A00E7332D /* UtfConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = 838490211807649A00E7332D /* UtfConverter.h */; };
838490461807649A00E7332D /* UTFEncodeDecode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 838490221807649A00E7332D /* UTFEncodeDecode.cpp */; };
838490471807649A00E7332D /* UTFEncodeDecode.h in Headers */ = {isa = PBXBuildFile; fileRef = 838490231807649A00E7332D /* UTFEncodeDecode.h */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
83848FB81807623F00E7332D /* SSEQPlayer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SSEQPlayer.framework; sourceTree = BUILT_PRODUCTS_DIR; };
83848FBB1807623F00E7332D /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
83848FBE1807623F00E7332D /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
83848FBF1807623F00E7332D /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
83848FC01807623F00E7332D /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
83848FC31807623F00E7332D /* SSEQPlayer-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SSEQPlayer-Info.plist"; sourceTree = "<group>"; };
83848FC51807623F00E7332D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
83848FD01807623F00E7332D /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
838490001807649A00E7332D /* BigSString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BigSString.h; sourceTree = "<group>"; };
838490011807649A00E7332D /* Channel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Channel.cpp; sourceTree = "<group>"; };
838490021807649A00E7332D /* Channel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Channel.h; sourceTree = "<group>"; };
838490031807649A00E7332D /* common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = "<group>"; };
838490041807649A00E7332D /* consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = consts.h; sourceTree = "<group>"; };
838490051807649A00E7332D /* convert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = convert.h; sourceTree = "<group>"; };
838490061807649A00E7332D /* ConvertUTF.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConvertUTF.cpp; sourceTree = "<group>"; };
838490071807649A00E7332D /* ConvertUTF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConvertUTF.h; sourceTree = "<group>"; };
838490081807649A00E7332D /* FATSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FATSection.cpp; sourceTree = "<group>"; };
838490091807649A00E7332D /* FATSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FATSection.h; sourceTree = "<group>"; };
8384900A1807649A00E7332D /* INFOEntry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = INFOEntry.cpp; sourceTree = "<group>"; };
8384900B1807649A00E7332D /* INFOEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INFOEntry.h; sourceTree = "<group>"; };
8384900C1807649A00E7332D /* INFOSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = INFOSection.cpp; sourceTree = "<group>"; };
8384900D1807649A00E7332D /* INFOSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INFOSection.h; sourceTree = "<group>"; };
8384900E1807649A00E7332D /* NDSStdHeader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NDSStdHeader.cpp; sourceTree = "<group>"; };
8384900F1807649A00E7332D /* NDSStdHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NDSStdHeader.h; sourceTree = "<group>"; };
838490101807649A00E7332D /* Player.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Player.cpp; sourceTree = "<group>"; };
838490111807649A00E7332D /* Player.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Player.h; sourceTree = "<group>"; };
838490121807649A00E7332D /* SBNK.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SBNK.cpp; sourceTree = "<group>"; };
838490131807649A00E7332D /* SBNK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBNK.h; sourceTree = "<group>"; };
838490141807649A00E7332D /* SDAT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SDAT.cpp; sourceTree = "<group>"; };
838490151807649A00E7332D /* SDAT.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDAT.h; sourceTree = "<group>"; };
838490161807649A00E7332D /* SSEQ.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SSEQ.cpp; sourceTree = "<group>"; };
838490171807649A00E7332D /* SSEQ.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSEQ.h; sourceTree = "<group>"; };
838490181807649A00E7332D /* SWAR.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SWAR.cpp; sourceTree = "<group>"; };
838490191807649A00E7332D /* SWAR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWAR.h; sourceTree = "<group>"; };
8384901A1807649A00E7332D /* SWAV.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SWAV.cpp; sourceTree = "<group>"; };
8384901B1807649A00E7332D /* SWAV.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWAV.h; sourceTree = "<group>"; };
8384901C1807649A00E7332D /* SYMBSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SYMBSection.cpp; sourceTree = "<group>"; };
8384901D1807649A00E7332D /* SYMBSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SYMBSection.h; sourceTree = "<group>"; };
8384901E1807649A00E7332D /* Track.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Track.cpp; sourceTree = "<group>"; };
8384901F1807649A00E7332D /* Track.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Track.h; sourceTree = "<group>"; };
838490201807649A00E7332D /* UtfConverter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UtfConverter.cpp; sourceTree = "<group>"; };
838490211807649A00E7332D /* UtfConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UtfConverter.h; sourceTree = "<group>"; };
838490221807649A00E7332D /* UTFEncodeDecode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UTFEncodeDecode.cpp; sourceTree = "<group>"; };
838490231807649A00E7332D /* UTFEncodeDecode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UTFEncodeDecode.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
83848FB41807623F00E7332D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
83848FBC1807623F00E7332D /* Cocoa.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
83848FAE1807623F00E7332D = {
isa = PBXGroup;
children = (
83848FC11807623F00E7332D /* SSEQPlayer */,
83848FBA1807623F00E7332D /* Frameworks */,
83848FB91807623F00E7332D /* Products */,
);
sourceTree = "<group>";
};
83848FB91807623F00E7332D /* Products */ = {
isa = PBXGroup;
children = (
83848FB81807623F00E7332D /* SSEQPlayer.framework */,
);
name = Products;
sourceTree = "<group>";
};
83848FBA1807623F00E7332D /* Frameworks */ = {
isa = PBXGroup;
children = (
83848FBB1807623F00E7332D /* Cocoa.framework */,
83848FD01807623F00E7332D /* XCTest.framework */,
83848FBD1807623F00E7332D /* Other Frameworks */,
);
name = Frameworks;
sourceTree = "<group>";
};
83848FBD1807623F00E7332D /* Other Frameworks */ = {
isa = PBXGroup;
children = (
83848FBE1807623F00E7332D /* Foundation.framework */,
83848FBF1807623F00E7332D /* CoreData.framework */,
83848FC01807623F00E7332D /* AppKit.framework */,
);
name = "Other Frameworks";
sourceTree = "<group>";
};
83848FC11807623F00E7332D /* SSEQPlayer */ = {
isa = PBXGroup;
children = (
838490001807649A00E7332D /* BigSString.h */,
838490011807649A00E7332D /* Channel.cpp */,
838490021807649A00E7332D /* Channel.h */,
838490031807649A00E7332D /* common.h */,
838490041807649A00E7332D /* consts.h */,
838490051807649A00E7332D /* convert.h */,
838490061807649A00E7332D /* ConvertUTF.cpp */,
838490071807649A00E7332D /* ConvertUTF.h */,
838490081807649A00E7332D /* FATSection.cpp */,
838490091807649A00E7332D /* FATSection.h */,
8384900A1807649A00E7332D /* INFOEntry.cpp */,
8384900B1807649A00E7332D /* INFOEntry.h */,
8384900C1807649A00E7332D /* INFOSection.cpp */,
8384900D1807649A00E7332D /* INFOSection.h */,
8384900E1807649A00E7332D /* NDSStdHeader.cpp */,
8384900F1807649A00E7332D /* NDSStdHeader.h */,
838490101807649A00E7332D /* Player.cpp */,
838490111807649A00E7332D /* Player.h */,
838490121807649A00E7332D /* SBNK.cpp */,
838490131807649A00E7332D /* SBNK.h */,
838490141807649A00E7332D /* SDAT.cpp */,
838490151807649A00E7332D /* SDAT.h */,
838490161807649A00E7332D /* SSEQ.cpp */,
838490171807649A00E7332D /* SSEQ.h */,
838490181807649A00E7332D /* SWAR.cpp */,
838490191807649A00E7332D /* SWAR.h */,
8384901A1807649A00E7332D /* SWAV.cpp */,
8384901B1807649A00E7332D /* SWAV.h */,
8384901C1807649A00E7332D /* SYMBSection.cpp */,
8384901D1807649A00E7332D /* SYMBSection.h */,
8384901E1807649A00E7332D /* Track.cpp */,
8384901F1807649A00E7332D /* Track.h */,
838490201807649A00E7332D /* UtfConverter.cpp */,
838490211807649A00E7332D /* UtfConverter.h */,
838490221807649A00E7332D /* UTFEncodeDecode.cpp */,
838490231807649A00E7332D /* UTFEncodeDecode.h */,
83848FC21807623F00E7332D /* Supporting Files */,
);
path = SSEQPlayer;
sourceTree = "<group>";
};
83848FC21807623F00E7332D /* Supporting Files */ = {
isa = PBXGroup;
children = (
83848FC31807623F00E7332D /* SSEQPlayer-Info.plist */,
83848FC41807623F00E7332D /* InfoPlist.strings */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
83848FB51807623F00E7332D /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
8384902F1807649A00E7332D /* INFOEntry.h in Headers */,
838490281807649A00E7332D /* consts.h in Headers */,
838490261807649A00E7332D /* Channel.h in Headers */,
838490431807649A00E7332D /* Track.h in Headers */,
8384903B1807649A00E7332D /* SSEQ.h in Headers */,
838490371807649A00E7332D /* SBNK.h in Headers */,
8384903D1807649A00E7332D /* SWAR.h in Headers */,
8384903F1807649A00E7332D /* SWAV.h in Headers */,
838490271807649A00E7332D /* common.h in Headers */,
838490391807649A00E7332D /* SDAT.h in Headers */,
838490351807649A00E7332D /* Player.h in Headers */,
838490471807649A00E7332D /* UTFEncodeDecode.h in Headers */,
838490311807649A00E7332D /* INFOSection.h in Headers */,
8384902B1807649A00E7332D /* ConvertUTF.h in Headers */,
838490451807649A00E7332D /* UtfConverter.h in Headers */,
8384902D1807649A00E7332D /* FATSection.h in Headers */,
838490331807649A00E7332D /* NDSStdHeader.h in Headers */,
838490291807649A00E7332D /* convert.h in Headers */,
838490411807649A00E7332D /* SYMBSection.h in Headers */,
838490241807649A00E7332D /* BigSString.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
83848FB71807623F00E7332D /* SSEQPlayer */ = {
isa = PBXNativeTarget;
buildConfigurationList = 83848FE01807623F00E7332D /* Build configuration list for PBXNativeTarget "SSEQPlayer" */;
buildPhases = (
83848FB31807623F00E7332D /* Sources */,
83848FB41807623F00E7332D /* Frameworks */,
83848FB51807623F00E7332D /* Headers */,
83848FB61807623F00E7332D /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = SSEQPlayer;
productName = SSEQPlayer;
productReference = 83848FB81807623F00E7332D /* SSEQPlayer.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
83848FAF1807623F00E7332D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0500;
ORGANIZATIONNAME = "Christopher Snowhill";
};
buildConfigurationList = 83848FB21807623F00E7332D /* Build configuration list for PBXProject "SSEQPlayer" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 83848FAE1807623F00E7332D;
productRefGroup = 83848FB91807623F00E7332D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
83848FB71807623F00E7332D /* SSEQPlayer */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
83848FB61807623F00E7332D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
83848FC61807623F00E7332D /* InfoPlist.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
83848FB31807623F00E7332D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
838490441807649A00E7332D /* UtfConverter.cpp in Sources */,
838490381807649A00E7332D /* SDAT.cpp in Sources */,
8384902E1807649A00E7332D /* INFOEntry.cpp in Sources */,
838490341807649A00E7332D /* Player.cpp in Sources */,
838490301807649A00E7332D /* INFOSection.cpp in Sources */,
838490461807649A00E7332D /* UTFEncodeDecode.cpp in Sources */,
838490251807649A00E7332D /* Channel.cpp in Sources */,
8384903C1807649A00E7332D /* SWAR.cpp in Sources */,
8384903E1807649A00E7332D /* SWAV.cpp in Sources */,
838490421807649A00E7332D /* Track.cpp in Sources */,
8384902C1807649A00E7332D /* FATSection.cpp in Sources */,
838490321807649A00E7332D /* NDSStdHeader.cpp in Sources */,
8384902A1807649A00E7332D /* ConvertUTF.cpp in Sources */,
838490401807649A00E7332D /* SYMBSection.cpp in Sources */,
838490361807649A00E7332D /* SBNK.cpp in Sources */,
8384903A1807649A00E7332D /* SSEQ.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
83848FC41807623F00E7332D /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
83848FC51807623F00E7332D /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
83848FDE1807623F00E7332D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = NO;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
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;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.7;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
name = Debug;
};
83848FDF1807623F00E7332D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = NO;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_ENABLE_OBJC_EXCEPTIONS = 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;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.7;
SDKROOT = macosx;
};
name = Release;
};
83848FE11807623F00E7332D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "SSEQPlayer/SSEQPlayer-Info.plist";
INSTALL_PATH = "@loader_path/../Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
WRAPPER_EXTENSION = framework;
};
name = Debug;
};
83848FE21807623F00E7332D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = "SSEQPlayer/SSEQPlayer-Info.plist";
INSTALL_PATH = "@loader_path/../Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
WRAPPER_EXTENSION = framework;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
83848FB21807623F00E7332D /* Build configuration list for PBXProject "SSEQPlayer" */ = {
isa = XCConfigurationList;
buildConfigurations = (
83848FDE1807623F00E7332D /* Debug */,
83848FDF1807623F00E7332D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
83848FE01807623F00E7332D /* Build configuration list for PBXNativeTarget "SSEQPlayer" */ = {
isa = XCConfigurationList;
buildConfigurations = (
83848FE11807623F00E7332D /* Debug */,
83848FE21807623F00E7332D /* Release */,
);
defaultConfigurationIsVisible = 0;
};
/* End XCConfigurationList section */
};
rootObject = 83848FAF1807623F00E7332D /* Project object */;
}

View File

@ -0,0 +1,220 @@
/*
* String class in ANSI (or rather, current Windows code page), UTF-8, and UTF-16
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-03-25
*/
#ifndef BIGSSTRING_H
#define BIGSSTRING_H
#include <string>
#include <cstring>
#include <cwchar>
#include "UtfConverter.h"
#include "UTFEncodeDecode.h"
class String
{
public:
String(const std::locale &L = std::locale::classic()) : ansi(""), utf8(""), utf16(L""), loc(L) { }
String(const char *new_str, bool is_utf8 = false, const std::locale &L = std::locale::classic()) : ansi(is_utf8 ? DecodeFromUTF8(new_str, L) : new_str), utf8(is_utf8 ? new_str : EncodeToUTF8(new_str, L)),
utf16(UtfConverter::FromUtf8(utf8)), loc(L) { }
String(const std::string &new_str, bool is_utf8 = false, const std::locale &L = std::locale::classic()) : ansi(is_utf8 ? DecodeFromUTF8(new_str, L) : new_str), utf8(is_utf8 ? new_str : EncodeToUTF8(new_str, L)),
utf16(UtfConverter::FromUtf8(utf8)), loc(L) { }
String(const wchar_t *new_wstr, const std::locale &L = std::locale::classic()) : ansi(DecodeFromUTF8(UtfConverter::ToUtf8(new_wstr), L)), utf8(UtfConverter::ToUtf8(new_wstr)), utf16(new_wstr), loc(L) { }
String(const std::wstring &new_wstr, const std::locale &L = std::locale::classic()) : ansi(DecodeFromUTF8(UtfConverter::ToUtf8(new_wstr), L)), utf8(UtfConverter::ToUtf8(new_wstr)), utf16(new_wstr), loc(L) { }
bool empty() const { return this->utf8.empty(); }
std::string GetAnsi() const { return this->ansi; }
const char *GetAnsiC() const { return this->ansi.c_str(); }
std::string GetStr() const { return this->utf8; }
const char *GetStrC() const { return this->utf8.c_str(); }
std::wstring GetWStr() const { return this->utf16; }
const wchar_t *GetWStrC() const { return this->utf16.c_str(); }
bool operator==(const String &str2) const
{
return this->utf8 == str2.utf8;
}
String &operator=(const std::string &new_str)
{
this->ansi = DecodeFromUTF8(new_str, this->loc);
this->utf8 = new_str;
this->utf16 = UtfConverter::FromUtf8(new_str);
return *this;
}
String &operator=(const std::wstring &new_wstr)
{
this->utf8 = UtfConverter::ToUtf8(new_wstr);
this->ansi = DecodeFromUTF8(this->utf8, this->loc);
this->utf16 = new_wstr;
return *this;
}
String &operator=(const String &new_string)
{
if (this != &new_string)
{
this->ansi = new_string.ansi;
this->utf8 = new_string.utf8;
this->utf16 = new_string.utf16;
}
return *this;
}
String &SetISO8859_1(const std::string &new_str)
{
this->ansi = new_str;
this->utf8 = EncodeToUTF8(new_str, this->loc);
this->utf16 = UtfConverter::FromUtf8(this->utf8);
return *this;
}
String operator+(const String &str2) const
{
String new_string = String(*this);
new_string.ansi.append(str2.ansi);
new_string.utf8.append(str2.utf8);
new_string.utf16.append(str2.utf16);
return new_string;
}
String operator+(char chr) const
{
String new_string = String(*this);
char str2[] = { chr, 0 };
new_string.ansi.append(str2);
std::string new_utf8 = EncodeToUTF8(str2, this->loc);
new_string.utf8.append(new_utf8);
new_string.utf16.append(UtfConverter::FromUtf8(new_utf8));
return new_string;
}
String operator+(wchar_t wchr) const
{
String new_string = String(*this);
wchar_t wstr2[] = { wchr, 0 };
std::string new_utf8 = UtfConverter::ToUtf8(wstr2);
new_string.ansi.append(DecodeFromUTF8(new_utf8, this->loc));
new_string.utf8.append(new_utf8);
new_string.utf16.append(wstr2);
return new_string;
}
String operator+(const char *str2) const
{
String new_string = String(*this);
new_string.ansi.append(DecodeFromUTF8(str2, this->loc));
new_string.utf8.append(str2);
new_string.utf16.append(UtfConverter::FromUtf8(str2));
return new_string;
}
String operator+(const std::string &str2) const
{
String new_string = String(*this);
new_string.ansi.append(DecodeFromUTF8(str2, this->loc));
new_string.utf8.append(str2);
new_string.utf16.append(UtfConverter::FromUtf8(str2));
return new_string;
}
String operator+(const wchar_t *wstr2) const
{
String new_string = String(*this);
std::string new_utf8 = UtfConverter::ToUtf8(wstr2);
new_string.ansi.append(DecodeFromUTF8(new_utf8, this->loc));
new_string.utf8.append(new_utf8);
new_string.utf16.append(wstr2);
return new_string;
}
String operator+(const std::wstring &wstr2) const
{
String new_string = String(*this);
std::string new_utf8 = UtfConverter::ToUtf8(wstr2);
new_string.ansi.append(DecodeFromUTF8(new_utf8, this->loc));
new_string.utf8.append(new_utf8);
new_string.utf16.append(wstr2);
return new_string;
}
String &operator+=(const String &str2)
{
if (this != &str2)
{
this->ansi.append(str2.ansi);
this->utf8.append(str2.utf8);
this->utf16.append(str2.utf16);
}
return *this;
}
String &operator+=(char chr)
{
char str2[] = { chr, 0 };
this->ansi.append(str2);
std::string new_utf8 = EncodeToUTF8(str2, this->loc);
this->utf8.append(new_utf8);
this->utf16.append(UtfConverter::FromUtf8(new_utf8));
return *this;
}
String &operator+=(wchar_t wchr)
{
wchar_t wstr2[] = { wchr, 0 };
std::string new_utf8 = UtfConverter::ToUtf8(wstr2);
this->ansi.append(DecodeFromUTF8(new_utf8, this->loc));
this->utf8.append(new_utf8);
this->utf16.append(wstr2);
return *this;
}
String &operator+=(const char *str2)
{
this->ansi.append(DecodeFromUTF8(str2, this->loc));
this->utf8.append(str2);
this->utf16.append(UtfConverter::FromUtf8(str2));
return *this;
}
String &operator+=(const std::string &str2)
{
this->ansi.append(DecodeFromUTF8(str2, this->loc));
this->utf8.append(str2);
this->utf16.append(UtfConverter::FromUtf8(str2));
return *this;
}
String &operator+=(const wchar_t *wstr2)
{
std::string new_utf8 = UtfConverter::ToUtf8(wstr2);
this->ansi.append(DecodeFromUTF8(new_utf8, this->loc));
this->utf8.append(new_utf8);
this->utf16.append(wstr2);
return *this;
}
String &operator+=(const std::wstring &wstr2)
{
std::string new_utf8 = UtfConverter::ToUtf8(wstr2);
this->ansi.append(DecodeFromUTF8(new_utf8, this->loc));
this->utf8.append(new_utf8);
this->utf16.append(wstr2);
return *this;
}
String &AppendISO8859_1(const std::string &str2)
{
this->ansi.append(str2);
std::string new_utf8 = EncodeToUTF8(str2, this->loc);
this->utf8.append(new_utf8);
this->utf16.append(UtfConverter::FromUtf8(new_utf8));
return *this;
}
String Substring(size_t pos = 0, size_t n = std::string::npos) const
{
String new_string = String(*this);
new_string.ansi.substr(pos, n);
new_string.utf8.substr(pos, n);
if (n == std::string::npos)
n = std::wstring::npos;
new_string.utf16.substr(pos, n);
return new_string;
}
void CopyToString(wchar_t *str, bool = false) const
{
wcscpy(str, utf16.c_str());
}
void CopyToString(char *str, bool use_utf8 = false) const
{
strcpy(str, (use_utf8 ? utf8 : ansi).c_str());
}
protected:
std::string ansi, utf8;
std::wstring utf16;
std::locale loc;
};
#endif

View File

@ -0,0 +1,772 @@
/*
* SSEQ Player - Channel structures
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-04-23
*
* Adapted from source code of FeOS Sound System
* By fincs
* https://github.com/fincs/FSS
*
* Some code/concepts from DeSmuME
* http://desmume.org/
*/
#define _USE_MATH_DEFINES
#include <cstdlib>
#include <cmath>
#include <limits>
#include "Channel.h"
#include "Player.h"
#include "common.h"
NDSSoundRegister::NDSSoundRegister() : volumeMul(0), volumeDiv(0), panning(0), waveDuty(0), repeatMode(0), format(0), enable(false),
source(nullptr), timer(0), psgX(0), psgLast(0), psgLastCount(0), samplePosition(0), sampleIncrease(0), loopStart(0), length(0), totalLength(0)
{
}
void NDSSoundRegister::ClearControlRegister()
{
this->volumeMul = this->volumeDiv = this->panning = this->waveDuty = this->repeatMode = this->format = 0;
this->enable = false;
}
void NDSSoundRegister::SetControlRegister(uint32_t reg)
{
this->volumeMul = reg & 0x7F;
this->volumeDiv = (reg >> 8) & 0x03;
this->panning = (reg >> 16) & 0x7F;
this->waveDuty = (reg >> 24) & 0x07;
this->repeatMode = (reg >> 27) & 0x03;
this->format = (reg >> 29) & 0x03;
this->enable = (reg >> 31) & 0x01;
}
TempSndReg::TempSndReg() : CR(0), SOURCE(nullptr), TIMER(0), REPEAT_POINT(0), LENGTH(0)
{
}
bool Channel::initializedLUTs = false;
double Channel::cosine_lut[Channel::COSINE_RESOLUTION];
double Channel::lanczos_lut[Channel::LANCZOS_SAMPLES + 1];
#ifndef M_PI
static const double M_PI = 3.14159265358979323846;
#endif
// Code from http://learningcppisfun.blogspot.com/2010/04/comparing-floating-point-numbers.html
template<typename T> inline bool fEqual(T x, T y, int N = 1)
{
T diff = std::abs(x - y);
T tolerance = N * std::numeric_limits<T>::epsilon();
return diff <= tolerance * std::abs(x) && diff <= tolerance * std::abs(y);
}
static inline double sinc(double x)
{
return fEqual(x, 0.0) ? 1.0 : std::sin(x * M_PI) / (x * M_PI);
}
Channel::Channel() : chnId(-1), tempReg(), state(CS_NONE), trackId(-1), prio(0), manualSweep(false), flags(), pan(0), extAmpl(0), velocity(0), extPan(0),
key(0), ampl(0), extTune(0), orgKey(0), modType(0), modSpeed(0), modDepth(0), modRange(0), modDelay(0), modDelayCnt(0), modCounter(0),
sweepLen(0), sweepCnt(0), sweepPitch(0), attackLvl(0), sustainLvl(0x7F), decayRate(0), releaseRate(0xFFFF), noteLength(-1), vol(0), ply(nullptr), reg()
{
this->clearHistory();
if (!this->initializedLUTs)
{
for (unsigned i = 0; i < COSINE_RESOLUTION; ++i)
this->cosine_lut[i] = (1.0 - std::cos((static_cast<double>(i) / COSINE_RESOLUTION) * M_PI)) * 0.5;
double dx = static_cast<double>(LANCZOS_WIDTH) / LANCZOS_SAMPLES, x = 0.0;
for (unsigned i = 0; i <= LANCZOS_SAMPLES; ++i, x += dx)
this->lanczos_lut[i] = std::abs(x) < LANCZOS_WIDTH ? sinc(x) * sinc(x / LANCZOS_WIDTH) : 0.0;
this->initializedLUTs = true;
}
}
void Channel::UpdateVol(const Track &trk)
{
int finalVol = trk.ply->masterVol;
finalVol += Cnv_Sust(trk.vol);
finalVol += Cnv_Sust(trk.expr);
if (finalVol < -AMPL_K)
finalVol = -AMPL_K;
this->extAmpl = finalVol;
}
void Channel::UpdatePan(const Track &trk)
{
this->extPan = trk.pan;
}
void Channel::UpdateTune(const Track &trk)
{
int tune = (static_cast<int>(this->key) - static_cast<int>(this->orgKey)) * 64;
tune += (static_cast<int>(trk.pitchBend) * static_cast<int>(trk.pitchBendRange)) >> 1;
this->extTune = tune;
}
void Channel::UpdateMod(const Track &trk)
{
this->modType = trk.modType;
this->modSpeed = trk.modSpeed;
this->modDepth = trk.modDepth;
this->modRange = trk.modRange;
this->modDelay = trk.modDelay;
}
void Channel::UpdatePorta(const Track &trk)
{
this->manualSweep = false;
this->sweepPitch = trk.sweepPitch;
this->sweepCnt = 0;
if (!trk.state[TS_PORTABIT])
{
this->sweepLen = 0;
return;
}
int diff = (static_cast<int>(trk.portaKey) - static_cast<int>(this->key)) << 22;
this->sweepPitch += diff >> 16;
if (!trk.portaTime)
{
this->sweepLen = this->noteLength;
this->manualSweep = true;
}
else
{
int sq_time = static_cast<uint32_t>(trk.portaTime) * static_cast<uint32_t>(trk.portaTime);
long abs_sp = std::abs((long)this->sweepPitch);
this->sweepLen = (abs_sp * sq_time) >> 11;
}
}
void Channel::Release()
{
this->noteLength = -1;
this->prio = 1;
this->state = CS_RELEASE;
}
void Channel::Kill()
{
this->state = CS_NONE;
this->trackId = -1;
this->prio = 0;
this->reg.ClearControlRegister();
this->vol = 0;
this->noteLength = -1;
this->clearHistory();
}
static inline int getModFlag(int type)
{
switch (type)
{
case 0:
return CF_UPDTMR;
case 1:
return CF_UPDVOL;
case 2:
return CF_UPDPAN;
default:
return 0;
}
}
void Channel::UpdateTrack()
{
if (!this->ply)
return;
int trkn = this->trackId;
if (trkn == -1)
return;
auto &trackFlags = this->ply->tracks[trkn].updateFlags;
if (trackFlags.none())
return;
auto &trk = this->ply->tracks[trkn];
if (trackFlags[TUF_LEN])
{
int st = this->state;
if (st > CS_START)
{
if (st < CS_RELEASE && !--this->noteLength)
this->Release();
if (this->manualSweep && this->sweepCnt < this->sweepLen)
++this->sweepCnt;
}
}
if (trackFlags[TUF_VOL])
{
this->UpdateVol(trk);
this->flags.set(CF_UPDVOL);
}
if (trackFlags[TUF_PAN])
{
this->UpdatePan(trk);
this->flags.set(CF_UPDPAN);
}
if (trackFlags[TUF_TIMER])
{
this->UpdateTune(trk);
this->flags.set(CF_UPDTMR);
}
if (trackFlags[TUF_MOD])
{
int oldType = this->modType;
int newType = trk.modType;
this->UpdateMod(trk);
if (oldType != newType)
{
this->flags.set(getModFlag(oldType));
this->flags.set(getModFlag(newType));
}
}
}
static const uint16_t getpitchtbl[] =
{
0x0000, 0x003B, 0x0076, 0x00B2, 0x00ED, 0x0128, 0x0164, 0x019F,
0x01DB, 0x0217, 0x0252, 0x028E, 0x02CA, 0x0305, 0x0341, 0x037D,
0x03B9, 0x03F5, 0x0431, 0x046E, 0x04AA, 0x04E6, 0x0522, 0x055F,
0x059B, 0x05D8, 0x0614, 0x0651, 0x068D, 0x06CA, 0x0707, 0x0743,
0x0780, 0x07BD, 0x07FA, 0x0837, 0x0874, 0x08B1, 0x08EF, 0x092C,
0x0969, 0x09A7, 0x09E4, 0x0A21, 0x0A5F, 0x0A9C, 0x0ADA, 0x0B18,
0x0B56, 0x0B93, 0x0BD1, 0x0C0F, 0x0C4D, 0x0C8B, 0x0CC9, 0x0D07,
0x0D45, 0x0D84, 0x0DC2, 0x0E00, 0x0E3F, 0x0E7D, 0x0EBC, 0x0EFA,
0x0F39, 0x0F78, 0x0FB6, 0x0FF5, 0x1034, 0x1073, 0x10B2, 0x10F1,
0x1130, 0x116F, 0x11AE, 0x11EE, 0x122D, 0x126C, 0x12AC, 0x12EB,
0x132B, 0x136B, 0x13AA, 0x13EA, 0x142A, 0x146A, 0x14A9, 0x14E9,
0x1529, 0x1569, 0x15AA, 0x15EA, 0x162A, 0x166A, 0x16AB, 0x16EB,
0x172C, 0x176C, 0x17AD, 0x17ED, 0x182E, 0x186F, 0x18B0, 0x18F0,
0x1931, 0x1972, 0x19B3, 0x19F5, 0x1A36, 0x1A77, 0x1AB8, 0x1AFA,
0x1B3B, 0x1B7D, 0x1BBE, 0x1C00, 0x1C41, 0x1C83, 0x1CC5, 0x1D07,
0x1D48, 0x1D8A, 0x1DCC, 0x1E0E, 0x1E51, 0x1E93, 0x1ED5, 0x1F17,
0x1F5A, 0x1F9C, 0x1FDF, 0x2021, 0x2064, 0x20A6, 0x20E9, 0x212C,
0x216F, 0x21B2, 0x21F5, 0x2238, 0x227B, 0x22BE, 0x2301, 0x2344,
0x2388, 0x23CB, 0x240E, 0x2452, 0x2496, 0x24D9, 0x251D, 0x2561,
0x25A4, 0x25E8, 0x262C, 0x2670, 0x26B4, 0x26F8, 0x273D, 0x2781,
0x27C5, 0x280A, 0x284E, 0x2892, 0x28D7, 0x291C, 0x2960, 0x29A5,
0x29EA, 0x2A2F, 0x2A74, 0x2AB9, 0x2AFE, 0x2B43, 0x2B88, 0x2BCD,
0x2C13, 0x2C58, 0x2C9D, 0x2CE3, 0x2D28, 0x2D6E, 0x2DB4, 0x2DF9,
0x2E3F, 0x2E85, 0x2ECB, 0x2F11, 0x2F57, 0x2F9D, 0x2FE3, 0x302A,
0x3070, 0x30B6, 0x30FD, 0x3143, 0x318A, 0x31D0, 0x3217, 0x325E,
0x32A5, 0x32EC, 0x3332, 0x3379, 0x33C1, 0x3408, 0x344F, 0x3496,
0x34DD, 0x3525, 0x356C, 0x35B4, 0x35FB, 0x3643, 0x368B, 0x36D3,
0x371A, 0x3762, 0x37AA, 0x37F2, 0x383A, 0x3883, 0x38CB, 0x3913,
0x395C, 0x39A4, 0x39ED, 0x3A35, 0x3A7E, 0x3AC6, 0x3B0F, 0x3B58,
0x3BA1, 0x3BEA, 0x3C33, 0x3C7C, 0x3CC5, 0x3D0E, 0x3D58, 0x3DA1,
0x3DEA, 0x3E34, 0x3E7D, 0x3EC7, 0x3F11, 0x3F5A, 0x3FA4, 0x3FEE,
0x4038, 0x4082, 0x40CC, 0x4116, 0x4161, 0x41AB, 0x41F5, 0x4240,
0x428A, 0x42D5, 0x431F, 0x436A, 0x43B5, 0x4400, 0x444B, 0x4495,
0x44E1, 0x452C, 0x4577, 0x45C2, 0x460D, 0x4659, 0x46A4, 0x46F0,
0x473B, 0x4787, 0x47D3, 0x481E, 0x486A, 0x48B6, 0x4902, 0x494E,
0x499A, 0x49E6, 0x4A33, 0x4A7F, 0x4ACB, 0x4B18, 0x4B64, 0x4BB1,
0x4BFE, 0x4C4A, 0x4C97, 0x4CE4, 0x4D31, 0x4D7E, 0x4DCB, 0x4E18,
0x4E66, 0x4EB3, 0x4F00, 0x4F4E, 0x4F9B, 0x4FE9, 0x5036, 0x5084,
0x50D2, 0x5120, 0x516E, 0x51BC, 0x520A, 0x5258, 0x52A6, 0x52F4,
0x5343, 0x5391, 0x53E0, 0x542E, 0x547D, 0x54CC, 0x551A, 0x5569,
0x55B8, 0x5607, 0x5656, 0x56A5, 0x56F4, 0x5744, 0x5793, 0x57E2,
0x5832, 0x5882, 0x58D1, 0x5921, 0x5971, 0x59C1, 0x5A10, 0x5A60,
0x5AB0, 0x5B01, 0x5B51, 0x5BA1, 0x5BF1, 0x5C42, 0x5C92, 0x5CE3,
0x5D34, 0x5D84, 0x5DD5, 0x5E26, 0x5E77, 0x5EC8, 0x5F19, 0x5F6A,
0x5FBB, 0x600D, 0x605E, 0x60B0, 0x6101, 0x6153, 0x61A4, 0x61F6,
0x6248, 0x629A, 0x62EC, 0x633E, 0x6390, 0x63E2, 0x6434, 0x6487,
0x64D9, 0x652C, 0x657E, 0x65D1, 0x6624, 0x6676, 0x66C9, 0x671C,
0x676F, 0x67C2, 0x6815, 0x6869, 0x68BC, 0x690F, 0x6963, 0x69B6,
0x6A0A, 0x6A5E, 0x6AB1, 0x6B05, 0x6B59, 0x6BAD, 0x6C01, 0x6C55,
0x6CAA, 0x6CFE, 0x6D52, 0x6DA7, 0x6DFB, 0x6E50, 0x6EA4, 0x6EF9,
0x6F4E, 0x6FA3, 0x6FF8, 0x704D, 0x70A2, 0x70F7, 0x714D, 0x71A2,
0x71F7, 0x724D, 0x72A2, 0x72F8, 0x734E, 0x73A4, 0x73FA, 0x7450,
0x74A6, 0x74FC, 0x7552, 0x75A8, 0x75FF, 0x7655, 0x76AC, 0x7702,
0x7759, 0x77B0, 0x7807, 0x785E, 0x78B4, 0x790C, 0x7963, 0x79BA,
0x7A11, 0x7A69, 0x7AC0, 0x7B18, 0x7B6F, 0x7BC7, 0x7C1F, 0x7C77,
0x7CCF, 0x7D27, 0x7D7F, 0x7DD7, 0x7E2F, 0x7E88, 0x7EE0, 0x7F38,
0x7F91, 0x7FEA, 0x8042, 0x809B, 0x80F4, 0x814D, 0x81A6, 0x81FF,
0x8259, 0x82B2, 0x830B, 0x8365, 0x83BE, 0x8418, 0x8472, 0x84CB,
0x8525, 0x857F, 0x85D9, 0x8633, 0x868E, 0x86E8, 0x8742, 0x879D,
0x87F7, 0x8852, 0x88AC, 0x8907, 0x8962, 0x89BD, 0x8A18, 0x8A73,
0x8ACE, 0x8B2A, 0x8B85, 0x8BE0, 0x8C3C, 0x8C97, 0x8CF3, 0x8D4F,
0x8DAB, 0x8E07, 0x8E63, 0x8EBF, 0x8F1B, 0x8F77, 0x8FD4, 0x9030,
0x908C, 0x90E9, 0x9146, 0x91A2, 0x91FF, 0x925C, 0x92B9, 0x9316,
0x9373, 0x93D1, 0x942E, 0x948C, 0x94E9, 0x9547, 0x95A4, 0x9602,
0x9660, 0x96BE, 0x971C, 0x977A, 0x97D8, 0x9836, 0x9895, 0x98F3,
0x9952, 0x99B0, 0x9A0F, 0x9A6E, 0x9ACD, 0x9B2C, 0x9B8B, 0x9BEA,
0x9C49, 0x9CA8, 0x9D08, 0x9D67, 0x9DC7, 0x9E26, 0x9E86, 0x9EE6,
0x9F46, 0x9FA6, 0xA006, 0xA066, 0xA0C6, 0xA127, 0xA187, 0xA1E8,
0xA248, 0xA2A9, 0xA30A, 0xA36B, 0xA3CC, 0xA42D, 0xA48E, 0xA4EF,
0xA550, 0xA5B2, 0xA613, 0xA675, 0xA6D6, 0xA738, 0xA79A, 0xA7FC,
0xA85E, 0xA8C0, 0xA922, 0xA984, 0xA9E7, 0xAA49, 0xAAAC, 0xAB0E,
0xAB71, 0xABD4, 0xAC37, 0xAC9A, 0xACFD, 0xAD60, 0xADC3, 0xAE27,
0xAE8A, 0xAEED, 0xAF51, 0xAFB5, 0xB019, 0xB07C, 0xB0E0, 0xB145,
0xB1A9, 0xB20D, 0xB271, 0xB2D6, 0xB33A, 0xB39F, 0xB403, 0xB468,
0xB4CD, 0xB532, 0xB597, 0xB5FC, 0xB662, 0xB6C7, 0xB72C, 0xB792,
0xB7F7, 0xB85D, 0xB8C3, 0xB929, 0xB98F, 0xB9F5, 0xBA5B, 0xBAC1,
0xBB28, 0xBB8E, 0xBBF5, 0xBC5B, 0xBCC2, 0xBD29, 0xBD90, 0xBDF7,
0xBE5E, 0xBEC5, 0xBF2C, 0xBF94, 0xBFFB, 0xC063, 0xC0CA, 0xC132,
0xC19A, 0xC202, 0xC26A, 0xC2D2, 0xC33A, 0xC3A2, 0xC40B, 0xC473,
0xC4DC, 0xC544, 0xC5AD, 0xC616, 0xC67F, 0xC6E8, 0xC751, 0xC7BB,
0xC824, 0xC88D, 0xC8F7, 0xC960, 0xC9CA, 0xCA34, 0xCA9E, 0xCB08,
0xCB72, 0xCBDC, 0xCC47, 0xCCB1, 0xCD1B, 0xCD86, 0xCDF1, 0xCE5B,
0xCEC6, 0xCF31, 0xCF9C, 0xD008, 0xD073, 0xD0DE, 0xD14A, 0xD1B5,
0xD221, 0xD28D, 0xD2F8, 0xD364, 0xD3D0, 0xD43D, 0xD4A9, 0xD515,
0xD582, 0xD5EE, 0xD65B, 0xD6C7, 0xD734, 0xD7A1, 0xD80E, 0xD87B,
0xD8E9, 0xD956, 0xD9C3, 0xDA31, 0xDA9E, 0xDB0C, 0xDB7A, 0xDBE8,
0xDC56, 0xDCC4, 0xDD32, 0xDDA0, 0xDE0F, 0xDE7D, 0xDEEC, 0xDF5B,
0xDFC9, 0xE038, 0xE0A7, 0xE116, 0xE186, 0xE1F5, 0xE264, 0xE2D4,
0xE343, 0xE3B3, 0xE423, 0xE493, 0xE503, 0xE573, 0xE5E3, 0xE654,
0xE6C4, 0xE735, 0xE7A5, 0xE816, 0xE887, 0xE8F8, 0xE969, 0xE9DA,
0xEA4B, 0xEABC, 0xEB2E, 0xEB9F, 0xEC11, 0xEC83, 0xECF5, 0xED66,
0xEDD9, 0xEE4B, 0xEEBD, 0xEF2F, 0xEFA2, 0xF014, 0xF087, 0xF0FA,
0xF16D, 0xF1E0, 0xF253, 0xF2C6, 0xF339, 0xF3AD, 0xF420, 0xF494,
0xF507, 0xF57B, 0xF5EF, 0xF663, 0xF6D7, 0xF74C, 0xF7C0, 0xF834,
0xF8A9, 0xF91E, 0xF992, 0xFA07, 0xFA7C, 0xFAF1, 0xFB66, 0xFBDC,
0xFC51, 0xFCC7, 0xFD3C, 0xFDB2, 0xFE28, 0xFE9E, 0xFF14, 0xFF8A
};
static const uint8_t getvoltbl[] =
{
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
0x09, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0E,
0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x14,
0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x18,
0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x1A, 0x1A, 0x1A, 0x1B, 0x1B, 0x1B, 0x1C, 0x1C, 0x1C,
0x1D, 0x1D, 0x1D, 0x1E, 0x1E, 0x1E, 0x1F, 0x1F, 0x1F, 0x20, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22,
0x22, 0x23, 0x23, 0x24, 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x27, 0x28, 0x28, 0x29,
0x29, 0x2A, 0x2A, 0x2B, 0x2B, 0x2C, 0x2C, 0x2D, 0x2D, 0x2E, 0x2E, 0x2F, 0x2F, 0x30, 0x31, 0x31,
0x32, 0x32, 0x33, 0x33, 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x38, 0x38, 0x39, 0x3A, 0x3A, 0x3B,
0x3C, 0x3C, 0x3D, 0x3E, 0x3F, 0x3F, 0x40, 0x41, 0x42, 0x42, 0x43, 0x44, 0x45, 0x45, 0x46, 0x47,
0x48, 0x49, 0x4A, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x52, 0x53, 0x54, 0x55,
0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x67,
0x68, 0x69, 0x6A, 0x6B, 0x6D, 0x6E, 0x6F, 0x71, 0x72, 0x73, 0x75, 0x76, 0x77, 0x79, 0x7A, 0x7B,
0x7D, 0x7E, 0x7F, 0x20, 0x21, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25,
0x26, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B, 0x2C, 0x2C, 0x2D,
0x2D, 0x2E, 0x2E, 0x2F, 0x2F, 0x30, 0x30, 0x31, 0x31, 0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x36,
0x36, 0x37, 0x37, 0x38, 0x39, 0x39, 0x3A, 0x3B, 0x3B, 0x3C, 0x3D, 0x3E, 0x3E, 0x3F, 0x40, 0x40,
0x41, 0x42, 0x43, 0x43, 0x44, 0x45, 0x46, 0x47, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4D,
0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D,
0x5E, 0x5F, 0x60, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6F, 0x70,
0x71, 0x73, 0x74, 0x75, 0x77, 0x78, 0x79, 0x7B, 0x7C, 0x7E, 0x7E, 0x40, 0x41, 0x42, 0x43, 0x43,
0x44, 0x45, 0x46, 0x47, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51,
0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61,
0x62, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x71, 0x72, 0x74, 0x75,
0x76, 0x78, 0x79, 0x7B, 0x7C, 0x7D, 0x7E, 0x40, 0x41, 0x42, 0x42, 0x43, 0x44, 0x45, 0x46, 0x46,
0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x65, 0x66,
0x67, 0x68, 0x69, 0x6A, 0x6C, 0x6D, 0x6E, 0x6F, 0x71, 0x72, 0x73, 0x75, 0x76, 0x77, 0x79, 0x7A,
0x7C, 0x7D, 0x7E, 0x7F
};
// This function was obtained through disassembly of Ninty's sound driver
static inline uint16_t Timer_Adjust(uint16_t basetmr, int pitch)
{
int shift = 0;
pitch = -pitch;
while (pitch < 0)
{
--shift;
pitch += 0x300;
}
while (pitch >= 0x300)
{
++shift;
pitch -= 0x300;
}
uint64_t tmr = static_cast<uint64_t>(basetmr) * (static_cast<uint32_t>(getpitchtbl[pitch]) + 0x10000);
shift -= 16;
if (shift <= 0)
tmr >>= -shift;
else if (shift < 32)
{
if (tmr & ((~0ULL) << (32 - shift)))
return 0xFFFF;
tmr <<= shift;
}
else
return 0x10;
if (tmr < 0x10)
return 0x10;
if (tmr > 0xFFFF)
return 0xFFFF;
return static_cast<uint16_t>(tmr);
}
static inline int calcVolDivShift(int x)
{
// VOLDIV(0) /1 >>0
// VOLDIV(1) /2 >>1
// VOLDIV(2) /4 >>2
// VOLDIV(3) /16 >>4
if (x < 3)
return x;
return 4;
}
void Channel::Update()
{
// Kill active channels that aren't physically active
if (this->state > CS_START && !this->reg.enable)
{
this->Kill();
return;
}
bool bNotInSustain = this->state != CS_SUSTAIN;
bool bInStart = this->state == CS_START;
bool bPitchSweep = this->sweepPitch && this->sweepLen && this->sweepCnt <= this->sweepLen;
bool bModulation = !!this->modDepth;
bool bVolNeedUpdate = this->flags[CF_UPDVOL] || bNotInSustain;
bool bPanNeedUpdate = this->flags[CF_UPDPAN] || bInStart;
bool bTmrNeedUpdate = this->flags[CF_UPDTMR] || bInStart || bPitchSweep;
int modParam = 0;
switch (this->state)
{
case CS_NONE:
return;
case CS_START:
this->reg.ClearControlRegister();
this->reg.source = this->tempReg.SOURCE;
this->reg.loopStart = this->tempReg.REPEAT_POINT;
this->reg.length = this->tempReg.LENGTH;
this->reg.totalLength = this->reg.loopStart + this->reg.length;
this->ampl = AMPL_THRESHOLD;
this->state = CS_ATTACK;
// Fall down
case CS_ATTACK:
this->ampl = (static_cast<int>(this->ampl) * static_cast<int>(this->attackLvl)) / 255;
if (!this->ampl)
this->state = CS_DECAY;
break;
case CS_DECAY:
{
this->ampl -= static_cast<int>(this->decayRate);
int sustLvl = Cnv_Sust(this->sustainLvl) << 7;
if (this->ampl <= sustLvl)
{
this->ampl = sustLvl;
this->state = CS_SUSTAIN;
}
break;
}
case CS_RELEASE:
this->ampl -= static_cast<int>(this->releaseRate);
if (this->ampl > AMPL_THRESHOLD)
break;
this->Kill();
return;
}
if (bModulation && this->modDelayCnt < this->modDelay)
{
++this->modDelayCnt;
bModulation = false;
}
if (bModulation)
{
switch (this->modType)
{
case 0:
bTmrNeedUpdate = true;
break;
case 1:
bVolNeedUpdate = true;
break;
case 2:
bPanNeedUpdate = true;
}
// Get the current modulation parameter
modParam = Cnv_Sine(this->modCounter >> 8) * this->modRange * this->modDepth;
if (!this->modType)
modParam = static_cast<int64_t>(modParam * 60) >> 14;
else
// This ugly formula whose exact meaning and workings I cannot figure out is used for volume/pan modulation.
modParam = ((modParam & ~0xFC000000) >> 8) | ((((modParam < 0 ? -1 : 0) << 6) | (static_cast<uint32_t>(modParam) >> 26)) << 18);
// Update the modulation variables
uint16_t speed = static_cast<uint16_t>(this->modSpeed) << 6;
uint16_t counter = (this->modCounter + speed) >> 8;
while (counter >= 0x80)
counter -= 0x80;
this->modCounter += speed;
this->modCounter &= 0xFF;
this->modCounter |= counter << 8;
}
if (bTmrNeedUpdate)
{
int totalAdj = this->extTune;
if (bModulation && !this->modType)
totalAdj += modParam;
if (bPitchSweep)
{
int len = this->sweepLen;
int cnt = this->sweepCnt;
totalAdj += (static_cast<int64_t>(this->sweepPitch) * (len - cnt)) / len;
if (!this->manualSweep)
++this->sweepCnt;
}
uint16_t tmr = this->tempReg.TIMER;
if (totalAdj)
tmr = Timer_Adjust(tmr, totalAdj);
this->reg.timer = -tmr;
this->reg.sampleIncrease = (ARM7_CLOCK / static_cast<double>(this->ply->sampleRate * 2)) / (0x10000 - this->reg.timer);
this->flags.reset(CF_UPDTMR);
}
if (bVolNeedUpdate || bPanNeedUpdate)
{
uint32_t cr = this->tempReg.CR;
if (bVolNeedUpdate)
{
int totalVol = this->ampl >> 7;
totalVol += this->extAmpl;
totalVol += this->velocity;
if (bModulation && this->modType == 1)
totalVol += modParam;
totalVol += AMPL_K;
if (totalVol < 0)
totalVol = 0;
cr &= ~(SOUND_VOL(0x7F) | SOUND_VOLDIV(3));
cr |= SOUND_VOL(static_cast<int>(getvoltbl[totalVol]));
if (totalVol < AMPL_K - 240)
cr |= SOUND_VOLDIV(3);
else if (totalVol < AMPL_K - 120)
cr |= SOUND_VOLDIV(2);
else if (totalVol < AMPL_K - 60)
cr |= SOUND_VOLDIV(1);
this->vol = ((cr & SOUND_VOL(0x7F)) << 4) >> calcVolDivShift((cr & SOUND_VOLDIV(3)) >> 8);
this->flags.reset(CF_UPDVOL);
}
if (bPanNeedUpdate)
{
int realPan = this->pan;
realPan += this->extPan;
if (bModulation && this->modType == 2)
realPan += modParam;
realPan += 64;
if (realPan < 0)
realPan = 0;
else if (realPan > 127)
realPan = 127;
cr &= ~SOUND_PAN(0x7F);
cr |= SOUND_PAN(realPan);
this->flags.reset(CF_UPDPAN);
}
this->tempReg.CR = cr;
this->reg.SetControlRegister(cr);
}
}
static const int16_t wavedutytbl[8][8] =
{
{ -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, 0x7FFF },
{ -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, 0x7FFF, 0x7FFF },
{ -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF },
{ -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF },
{ -0x7FFF, -0x7FFF, -0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF },
{ -0x7FFF, -0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF },
{ -0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF },
{ -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF }
};
// Linear and Cosine interpolation code originally from DeSmuME
// B-spline and Osculating come from Olli Niemitalo:
// http://www.student.oulu.fi/~oniemita/dsp/deip.pdf
int32_t Channel::Interpolate()
{
double ratio = this->reg.samplePosition;
ratio -= static_cast<int32_t>(ratio);
const auto &data = &this->sampleHistory[this->sampleHistoryPtr + 16];
if (this->ply->interpolation == INTERPOLATION_LANCZOS)
{
double kernel[LANCZOS_WIDTH * 2], kernel_sum = 0.0;
int i = LANCZOS_WIDTH, shift = static_cast<int>(std::floor(ratio * LANCZOS_RESOLUTION));
int step = this->reg.sampleIncrease > 1.0 ? static_cast<int>((1.0 / this->reg.sampleIncrease) * LANCZOS_RESOLUTION) : LANCZOS_RESOLUTION;
long shift_adj = shift * step / LANCZOS_RESOLUTION;
for (; i >= -static_cast<int>(LANCZOS_WIDTH - 1); --i)
{
long pos = i * step;
kernel_sum += kernel[i + LANCZOS_WIDTH - 1] = this->lanczos_lut[std::abs(shift_adj - pos)];
}
double sum = 0.0;
for (i = 0; i < static_cast<int>(LANCZOS_WIDTH * 2); ++i)
sum += data[i - static_cast<int>(LANCZOS_WIDTH) + 1] * kernel[i];
return static_cast<int32_t>(sum / kernel_sum);
}
else if (this->ply->interpolation > INTERPOLATION_COSINE)
{
double c0, c1, c2, c3, c4, c5;
if (this->ply->interpolation > INTERPOLATION_4POINTBSPLINE)
{
if (this->ply->interpolation == INTERPOLATION_6POINTBSPLINE)
{
double ym2py2 = data[-2] + data[2], ym1py1 = data[-1] + data[1];
double y2mym2 = data[2] - data[-2], y1mym1 = data[1] - data[-1];
double sixthym1py1 = 1 / 6.0 * ym1py1;
c0 = 1 / 120.0 * ym2py2 + 13 / 60.0 * ym1py1 + 0.55 * data[0];
c1 = 1 / 24.0 * y2mym2 + 5 / 12.0 * y1mym1;
c2 = 1 / 12.0 * ym2py2 + sixthym1py1 - 0.5 * data[0];
c3 = 1 / 12.0 * y2mym2 - 1 / 6.0 * y1mym1;
c4 = 1 / 24.0 * ym2py2 - sixthym1py1 + 0.25 * data[0];
c5 = 1 / 120.0 * (data[3] - data[-2]) + 1 / 24.0 * (data[-1] - data[2]) + 1 / 12.0 * (data[1] - data[0]);
return static_cast<int32_t>(((((c5 * ratio + c4) * ratio + c3) * ratio + c2) * ratio + c1) * ratio + c0);
}
else // INTERPOLATION_6POINTOSCULATING
{
ratio -= 0.5;
double even1 = data[-2] + data[3], odd1 = data[-2] - data[3];
double even2 = data[-1] + data[2], odd2 = data[-1] - data[2];
double even3 = data[0] + data[1], odd3 = data[0] - data[1];
c0 = 0.01171875 * even1 - 0.09765625 * even2 + 0.5859375 * even3;
c1 = 0.2109375 * odd2 - 281 / 192.0 * odd3 - 13 / 384.0 * odd1;
c2 = 0.40625 * even2 - 17 / 48.0 * even3 - 5 / 96.0 * even1;
c3 = 0.1875 * odd1 - 53 / 48.0 * odd2 + 2.375 * odd3;
c4 = 1 / 48.0 * even1 - 0.0625 * even2 + 1 / 24.0 * even3;
c5 = 25 / 24.0 * odd2 - 25 / 12.0 * odd3 - 5 / 24.0 * odd1;
return static_cast<int32_t>(((((c5 * ratio + c4) * ratio + c3) * ratio + c2) * ratio + c1) * ratio + c0);
}
}
else // INTERPOLATION_4POINTBSPLINE
{
double ym1py1 = data[-1] + data[1];
c0 = 1 / 6.0 * ym1py1 + 2 / 3.0 * data[0];
c1 = 0.5 * (data[1] - data[-1]);
c2 = 0.5 * ym1py1 - data[0];
c3 = 0.5 * (data[0] - data[1]) + 1 / 6.0 * (data[2] - data[-1]);
return static_cast<int32_t>(((c3 * ratio + c2) * ratio + c1) * ratio + c0);
}
}
else if (this->ply->interpolation == INTERPOLATION_COSINE)
return static_cast<int32_t>(data[0] + this->cosine_lut[static_cast<unsigned>(ratio * COSINE_RESOLUTION)] * (data[1] - data[0]));
else // INTERPOLATION_LINEAR
return static_cast<int32_t>(data[0] + ratio * (data[1] - data[0]));
}
int32_t Channel::GenerateSample()
{
if (this->reg.samplePosition < 0)
return 0;
if (this->reg.format != 3)
{
if (this->ply->interpolation == INTERPOLATION_NONE)
return this->reg.source->dataptr[static_cast<uint32_t>(this->reg.samplePosition)];
else
return this->Interpolate();
}
else
{
if (this->chnId < 8)
return 0;
else if (this->chnId < 14)
return wavedutytbl[this->reg.waveDuty][static_cast<uint32_t>(this->reg.samplePosition) & 0x7];
else
{
if (this->reg.psgLastCount != static_cast<uint32_t>(this->reg.samplePosition))
{
uint32_t max = static_cast<uint32_t>(this->reg.samplePosition);
for (uint32_t i = this->reg.psgLastCount; i < max; ++i)
{
if (this->reg.psgX & 0x1)
{
this->reg.psgX = (this->reg.psgX >> 1) ^ 0x6000;
this->reg.psgLast = -0x7FFF;
}
else
{
this->reg.psgX >>= 1;
this->reg.psgLast = 0x7FFF;
}
}
this->reg.psgLastCount = static_cast<uint32_t>(this->reg.samplePosition);
}
return this->reg.psgLast;
}
}
}
void Channel::IncrementSample()
{
double samplePosition = this->reg.samplePosition + this->reg.sampleIncrease;
if (this->reg.format != 3 && this->reg.samplePosition >= 0)
{
uint32_t loc = static_cast<uint32_t>(this->reg.samplePosition);
uint32_t newloc = static_cast<uint32_t>(samplePosition);
if (newloc >= this->reg.totalLength)
newloc -= this->reg.length;
while (loc != newloc)
{
this->sampleHistory[this->sampleHistoryPtr] = this->sampleHistory[this->sampleHistoryPtr + 32] = this->reg.source->dataptr[loc++];
this->sampleHistoryPtr = (this->sampleHistoryPtr + 1) & 31;
if (loc >= this->reg.totalLength)
loc -= this->reg.length;
}
}
this->reg.samplePosition = samplePosition;
if (this->reg.format != 3 && this->reg.samplePosition >= this->reg.totalLength)
{
if (this->reg.repeatMode == 1)
{
while (this->reg.samplePosition >= this->reg.totalLength)
this->reg.samplePosition -= this->reg.length;
}
else
this->Kill();
}
}
void Channel::clearHistory()
{
this->sampleHistoryPtr = 0;
memset(this->sampleHistory, 0, sizeof(this->sampleHistory));
}

View File

@ -0,0 +1,169 @@
/*
* SSEQ Player - Channel structures
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-04-02
*
* Adapted from source code of FeOS Sound System
* By fincs
* https://github.com/fincs/FSS
*
* Some code/concepts from DeSmuME
* http://desmume.org/
*/
#ifndef SSEQPLAYER_CHANNEL_H
#define SSEQPLAYER_CHANNEL_H
#include <bitset>
#include <tuple>
#include <cstdint>
#include "SWAV.h"
#include "Track.h"
/*
* This structure is meant to be similar to what is stored in the actual
* Nintendo DS's sound registers. Items that were not being used by this
* player have been removed, and items which help the simulated registers
* have been added.
*/
struct NDSSoundRegister
{
// Control Register
uint8_t volumeMul;
uint8_t volumeDiv;
uint8_t panning;
uint8_t waveDuty;
uint8_t repeatMode;
uint8_t format;
bool enable;
// Data Source Register
const SWAV *source;
// Timer Register
uint16_t timer;
// PSG Handling, not a DS register
uint16_t psgX;
int16_t psgLast;
uint32_t psgLastCount;
// The following are taken from DeSmuME
double samplePosition;
double sampleIncrease;
// Loopstart Register
uint32_t loopStart;
// Length Register
uint32_t length;
uint32_t totalLength;
NDSSoundRegister();
void ClearControlRegister();
void SetControlRegister(uint32_t reg);
};
/*
* From FeOS Sound System, this is temporary storage of what will go into
* the Nintendo DS sound registers. It is kept separate as the original code
* from FeOS Sound System utilized this to hold data prior to passing it into
* the DS's registers.
*/
struct TempSndReg
{
uint32_t CR;
const SWAV *SOURCE;
uint16_t TIMER;
uint32_t REPEAT_POINT, LENGTH;
TempSndReg();
};
struct Player;
struct Channel
{
int8_t chnId;
TempSndReg tempReg;
uint8_t state;
int8_t trackId; // -1 = none
uint8_t prio;
bool manualSweep;
std::bitset<CF_BITS> flags;
int8_t pan; // -64 .. 63
int16_t extAmpl;
int16_t velocity;
int8_t extPan;
uint8_t key;
int ampl; // 7 fractionary bits
int extTune; // in 64ths of a semitone
uint8_t orgKey;
uint8_t modType, modSpeed, modDepth, modRange;
uint16_t modDelay, modDelayCnt, modCounter;
uint32_t sweepLen, sweepCnt;
int16_t sweepPitch;
uint8_t attackLvl, sustainLvl;
uint16_t decayRate, releaseRate;
/*
* These were originally global variables in FeOS Sound System, but
* since they were linked to a certain channel anyways, I moved them
* into this class.
*/
int noteLength;
uint16_t vol;
const Player *ply;
NDSSoundRegister reg;
/*
* Interpolation history buffer, which contains the maximum number of
* samples required for any given interpolation mode. Doubled to
* simplify the case of wrapping. Thanks to kode54 for providing this.
*/
uint32_t sampleHistoryPtr;
int16_t sampleHistory[64];
/*
* Lookup tables for the cosine and Lanczos Sinc interpolations, to
* avoid the need to call the sin/cos functions all the time.
* These are static as they will not change between channels or runs
* of the program.
*/
static bool initializedLUTs;
static const unsigned COSINE_RESOLUTION = 8192;
static const unsigned LANCZOS_RESOLUTION = 8192;
static const unsigned LANCZOS_WIDTH = 8;
static const unsigned LANCZOS_SAMPLES = LANCZOS_RESOLUTION * LANCZOS_WIDTH;
static double cosine_lut[COSINE_RESOLUTION];
static double lanczos_lut[LANCZOS_SAMPLES + 1];
Channel();
void UpdateVol(const Track &trk);
void UpdatePan(const Track &trk);
void UpdateTune(const Track &trk);
void UpdateMod(const Track &trk);
void UpdatePorta(const Track &trk);
void Release();
void Kill();
void UpdateTrack();
void Update();
int32_t Interpolate();
int32_t GenerateSample();
void IncrementSample();
void clearHistory();
};
#endif

View File

@ -0,0 +1,602 @@
/*
* Copyright 2001-2004 Unicode, Inc.
*
* Disclaimer
*
* This source code is provided as is by Unicode, Inc. No claims are
* made as to fitness for any particular purpose. No warranties of any
* kind are expressed or implied. The recipient agrees to determine
* applicability of information provided. If this file has been
* purchased on magnetic or optical media from Unicode, Inc., the
* sole remedy for any claim will be exchange of defective media
* within 90 days of receipt.
*
* Limitations on Rights to Redistribute This Code
*
* Unicode, Inc. hereby grants the right to freely use the information
* supplied in this file in the creation of products supporting the
* Unicode Standard, and to make copies of this file in any form
* for internal or external distribution as long as this notice
* remains attached.
*/
/* ---------------------------------------------------------------------
Conversions between UTF32, UTF-16, and UTF-8. Source code file.
Author: Mark E. Davis, 1994.
Rev History: Rick McGowan, fixes & updates May 2001.
Sept 2001: fixed const & error conditions per
mods suggested by S. Parent & A. Lillich.
June 2002: Tim Dodd added detection and handling of incomplete
source sequences, enhanced error detection, added casts
to eliminate compiler warnings.
July 2003: slight mods to back out aggressive FFFE detection.
Jan 2004: updated switches in from-UTF8 conversions.
Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.
See the header file "ConvertUTF.h" for complete documentation.
------------------------------------------------------------------------ */
#include "ConvertUTF.h"
static const int halfShift = 10; /* used for shifting by 10 bits */
static const UTF32 halfBase = 0x0010000UL;
static const UTF32 halfMask = 0x3FFUL;
static const UTF32 UNI_SUR_HIGH_START = 0xD800;
static const UTF32 UNI_SUR_HIGH_END = 0xDBFF;
static const UTF32 UNI_SUR_LOW_START = 0xDC00;
static const UTF32 UNI_SUR_LOW_END = 0xDFFF;
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF32toUTF16(const UTF32 **sourceStart, const UTF32 *sourceEnd, UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags)
{
ConversionResult result = conversionOK;
const UTF32 *source = *sourceStart;
UTF16 *target = *targetStart;
while (source < sourceEnd)
{
if (target >= targetEnd)
{
result = targetExhausted;
break;
}
UTF32 ch = *source++;
if (ch <= UNI_MAX_BMP) /* Target is a character <= 0xFFFF */
{
/* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END)
{
if (flags == strictConversion)
{
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
else
*target++ = UNI_REPLACEMENT_CHAR;
}
else
*target++ = static_cast<UTF16>(ch); /* normal case */
}
else if (ch > UNI_MAX_LEGAL_UTF32)
{
if (flags == strictConversion)
result = sourceIllegal;
else
*target++ = UNI_REPLACEMENT_CHAR;
}
else
{
/* target is a character in range 0xFFFF - 0x10FFFF. */
if (target + 1 >= targetEnd)
{
--source; /* Back up source pointer! */
result = targetExhausted;
break;
}
ch -= halfBase;
*target++ = static_cast<UTF16>((ch >> halfShift) + UNI_SUR_HIGH_START);
*target++ = static_cast<UTF16>((ch & halfMask) + UNI_SUR_LOW_START);
}
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF16toUTF32(const UTF16 **sourceStart, const UTF16 *sourceEnd, UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags)
{
ConversionResult result = conversionOK;
const UTF16 *source = *sourceStart;
UTF32 *target = *targetStart;
while (source < sourceEnd)
{
const UTF16 *oldSource = source; /* In case we have to back up because of target overflow. */
UTF32 ch = *source++;
/* If we have a surrogate pair, convert to UTF32 first. */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END)
{
/* If the 16 bits following the high surrogate are in the source buffer... */
if (source < sourceEnd)
{
UTF32 ch2 = *source;
/* If it's a low surrogate, convert to UTF32. */
if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END)
{
ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + (ch2 - UNI_SUR_LOW_START) + halfBase;
++source;
}
else if (flags == strictConversion) /* it's an unpaired high surrogate */
{
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
}
else /* We don't have the 16 bits following the high surrogate. */
{
--source; /* return to the high surrogate */
result = sourceExhausted;
break;
}
}
else if (flags == strictConversion)
{
/* UTF-16 surrogate values are illegal in UTF-32 */
if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END)
{
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
}
if (target >= targetEnd)
{
source = oldSource; /* Back up source pointer! */
result = targetExhausted;
break;
}
*target++ = ch;
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
/*
* Index into the table below with the first byte of a UTF-8 sequence to
* get the number of trailing bytes that are supposed to follow it.
* Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
* left as-is for anyone who may want to do such conversion, which was
* allowed in earlier algorithms.
*/
static const char trailingBytesForUTF8[256] = {
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,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,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,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,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,0,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
};
/*
* Magic values subtracted from a buffer value during UTF8 conversion.
* This table contains as many values as there might be trailing bytes
* in a UTF-8 sequence.
*/
static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
/*
* Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
* into the first byte, depending on how many bytes follow. There are
* as many entries in this table as there are UTF-8 sequence types.
* (I.e., one byte sequence, two byte... etc.). Remember that sequencs
* for *legal* UTF-8 will be 4 or fewer bytes total.
*/
static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
/* --------------------------------------------------------------------- */
/* The interface converts a whole buffer to avoid function-call overhead.
* Constants have been gathered. Loops & conditionals have been removed as
* much as possible for efficiency, in favor of drop-through switches.
* (See "Note A" at the bottom of the file for equivalent code.)
* If your compiler supports it, the "isLegalUTF8" call can be turned
* into an inline function.
*/
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF16toUTF8(const UTF16 **sourceStart, const UTF16 *sourceEnd, UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags)
{
ConversionResult result = conversionOK;
const UTF16 *source = *sourceStart;
UTF8 *target = *targetStart;
while (source < sourceEnd)
{
const UTF32 byteMask = 0xBF, byteMark = 0x80;
const UTF16 *oldSource = source; /* In case we have to back up because of target overflow. */
UTF32 ch = *source++;
/* If we have a surrogate pair, convert to UTF32 first. */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END)
{
/* If the 16 bits following the high surrogate are in the source buffer... */
if (source < sourceEnd)
{
UTF32 ch2 = *source;
/* If it's a low surrogate, convert to UTF32. */
if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END)
{
ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + (ch2 - UNI_SUR_LOW_START) + halfBase;
++source;
}
else if (flags == strictConversion) /* it's an unpaired high surrogate */
{
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
}
else /* We don't have the 16 bits following the high surrogate. */
{
--source; /* return to the high surrogate */
result = sourceExhausted;
break;
}
}
else if (flags == strictConversion)
{
/* UTF-16 surrogate values are illegal in UTF-32 */
if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END)
{
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
}
/* Figure out how many bytes the result will require */
unsigned short bytesToWrite = 0;
if (ch < 0x80)
bytesToWrite = 1;
else if (ch < 0x800)
bytesToWrite = 2;
else if (ch < 0x10000)
bytesToWrite = 3;
else if (ch < 0x110000)
bytesToWrite = 4;
else
{
bytesToWrite = 3;
ch = UNI_REPLACEMENT_CHAR;
}
target += bytesToWrite;
if (target > targetEnd)
{
source = oldSource; /* Back up source pointer! */
target -= bytesToWrite;
result = targetExhausted;
break;
}
switch (bytesToWrite) /* note: everything falls through. */
{
case 4: *--target = static_cast<UTF8>((ch | byteMark) & byteMask); ch >>= 6;
case 3: *--target = static_cast<UTF8>((ch | byteMark) & byteMask); ch >>= 6;
case 2: *--target = static_cast<UTF8>((ch | byteMark) & byteMask); ch >>= 6;
case 1: *--target = static_cast<UTF8>(ch | firstByteMark[bytesToWrite]);
}
target += bytesToWrite;
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
/*
* Utility routine to tell whether a sequence of bytes is legal UTF-8.
* This must be called with the length pre-determined by the first byte.
* If not calling this from ConvertUTF8to*, then the length can be set by:
* length = trailingBytesForUTF8[*source]+1;
* and the sequence is illegal right away if there aren't that many bytes
* available.
* If presented with a length > 4, this returns false. The Unicode
* definition of UTF-8 goes up to 4-byte sequences.
*/
static bool isLegalUTF8(const UTF8 *source, int length)
{
const UTF8 *srcptr = source + length;
UTF8 a;
switch (length)
{
default: return false;
/* Everything else falls through when "true"... */
case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
case 2: if ((a = (*--srcptr)) > 0xBF) return false;
switch (*source)
{
/* no fall-through in this inner switch */
case 0xE0: if (a < 0xA0) return false; break;
case 0xED: if (a > 0x9F) return false; break;
case 0xF0: if (a < 0x90) return false; break;
case 0xF4: if (a > 0x8F) return false; break;
default: if (a < 0x80) return false;
}
case 1: if (*source >= 0x80 && *source < 0xC2) return false;
}
if (*source > 0xF4)
return false;
return true;
}
/* --------------------------------------------------------------------- */
/*
* Exported function to return whether a UTF-8 sequence is legal or not.
* This is not used here; it's just exported.
*/
bool isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd)
{
int length = trailingBytesForUTF8[*source] + 1;
if (source + length > sourceEnd)
return false;
return isLegalUTF8(source, length);
}
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF8toUTF16(const UTF8 **sourceStart, const UTF8 *sourceEnd, UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags)
{
ConversionResult result = conversionOK;
const UTF8 *source = *sourceStart;
UTF16 *target = *targetStart;
while (source < sourceEnd)
{
UTF32 ch = 0;
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
if (source + extraBytesToRead >= sourceEnd)
{
result = sourceExhausted;
break;
}
/* Do this check whether lenient or strict */
if (!isLegalUTF8(source, extraBytesToRead + 1))
{
result = sourceIllegal;
break;
}
/*
* The cases all fall through. See "Note A" below.
*/
switch (extraBytesToRead)
{
case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
case 3: ch += *source++; ch <<= 6;
case 2: ch += *source++; ch <<= 6;
case 1: ch += *source++; ch <<= 6;
case 0: ch += *source++;
}
ch -= offsetsFromUTF8[extraBytesToRead];
if (target >= targetEnd)
{
source -= extraBytesToRead + 1; /* Back up source pointer! */
result = targetExhausted;
break;
}
if (ch <= UNI_MAX_BMP) /* Target is a character <= 0xFFFF */
{
/* UTF-16 surrogate values are illegal in UTF-32 */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END)
{
if (flags == strictConversion)
{
source -= extraBytesToRead + 1; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
else
*target++ = UNI_REPLACEMENT_CHAR;
}
else
*target++ = static_cast<UTF16>(ch); /* normal case */
}
else if (ch > UNI_MAX_UTF16)
{
if (flags == strictConversion)
{
result = sourceIllegal;
source -= extraBytesToRead + 1; /* return to the start */
break; /* Bail out; shouldn't continue */
}
else
*target++ = UNI_REPLACEMENT_CHAR;
}
else
{
/* target is a character in range 0xFFFF - 0x10FFFF. */
if (target + 1 >= targetEnd)
{
source -= extraBytesToRead + 1; /* Back up source pointer! */
result = targetExhausted;
break;
}
ch -= halfBase;
*target++ = static_cast<UTF16>((ch >> halfShift) + UNI_SUR_HIGH_START);
*target++ = static_cast<UTF16>((ch & halfMask) + UNI_SUR_LOW_START);
}
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF32toUTF8(const UTF32 **sourceStart, const UTF32 *sourceEnd, UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags)
{
ConversionResult result = conversionOK;
const UTF32 *source = *sourceStart;
UTF8 *target = *targetStart;
while (source < sourceEnd)
{
const UTF32 byteMask = 0xBF, byteMark = 0x80;
UTF32 ch = *source++;
if (flags == strictConversion)
{
/* UTF-16 surrogate values are illegal in UTF-32 */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END)
{
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
}
/*
* Figure out how many bytes the result will require. Turn any
* illegally large UTF32 things (> Plane 17) into replacement chars.
*/
unsigned short bytesToWrite = 0;
if (ch < 0x80)
bytesToWrite = 1;
else if (ch < 0x800)
bytesToWrite = 2;
else if (ch < 0x10000)
bytesToWrite = 3;
else if (ch <= UNI_MAX_LEGAL_UTF32)
bytesToWrite = 4;
else
{
bytesToWrite = 3;
ch = UNI_REPLACEMENT_CHAR;
result = sourceIllegal;
}
target += bytesToWrite;
if (target > targetEnd)
{
--source; /* Back up source pointer! */
target -= bytesToWrite;
result = targetExhausted;
break;
}
switch (bytesToWrite) /* note: everything falls through. */
{
case 4: *--target = static_cast<UTF8>((ch | byteMark) & byteMask); ch >>= 6;
case 3: *--target = static_cast<UTF8>((ch | byteMark) & byteMask); ch >>= 6;
case 2: *--target = static_cast<UTF8>((ch | byteMark) & byteMask); ch >>= 6;
case 1: *--target = static_cast<UTF8>(ch | firstByteMark[bytesToWrite]);
}
target += bytesToWrite;
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF8toUTF32(const UTF8 **sourceStart, const UTF8 *sourceEnd, UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags)
{
ConversionResult result = conversionOK;
const UTF8 *source = *sourceStart;
UTF32 *target = *targetStart;
while (source < sourceEnd)
{
UTF32 ch = 0;
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
if (source + extraBytesToRead >= sourceEnd)
{
result = sourceExhausted;
break;
}
/* Do this check whether lenient or strict */
if (!isLegalUTF8(source, extraBytesToRead + 1))
{
result = sourceIllegal;
break;
}
/*
* The cases all fall through. See "Note A" below.
*/
switch (extraBytesToRead)
{
case 5: ch += *source++; ch <<= 6;
case 4: ch += *source++; ch <<= 6;
case 3: ch += *source++; ch <<= 6;
case 2: ch += *source++; ch <<= 6;
case 1: ch += *source++; ch <<= 6;
case 0: ch += *source++;
}
ch -= offsetsFromUTF8[extraBytesToRead];
if (target >= targetEnd)
{
source -= extraBytesToRead + 1; /* Back up the source pointer! */
result = targetExhausted;
break;
}
if (ch <= UNI_MAX_LEGAL_UTF32)
{
/*
* UTF-16 surrogate values are illegal in UTF-32, and anything
* over Plane 17 (> 0x10FFFF) is illegal.
*/
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END)
{
if (flags == strictConversion)
{
source -= extraBytesToRead + 1; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
else
*target++ = UNI_REPLACEMENT_CHAR;
}
else
*target++ = ch;
}
else /* i.e., ch > UNI_MAX_LEGAL_UTF32 */
{
result = sourceIllegal;
*target++ = UNI_REPLACEMENT_CHAR;
}
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* ---------------------------------------------------------------------
Note A.
The fall-through switches in UTF-8 reading code save a
temp variable, some decrements & conditionals. The switches
are equivalent to the following loop:
{
int tmpBytesToRead = extraBytesToRead+1;
do {
ch += *source++;
--tmpBytesToRead;
if (tmpBytesToRead) ch <<= 6;
} while (tmpBytesToRead > 0);
}
In UTF-8 writing code, the switches on "bytesToWrite" are
similarly unrolled loops.
--------------------------------------------------------------------- */

View File

@ -0,0 +1,134 @@
/*
* Copyright 2001-2004 Unicode, Inc.
*
* Disclaimer
*
* This source code is provided as is by Unicode, Inc. No claims are
* made as to fitness for any particular purpose. No warranties of any
* kind are expressed or implied. The recipient agrees to determine
* applicability of information provided. If this file has been
* purchased on magnetic or optical media from Unicode, Inc., the
* sole remedy for any claim will be exchange of defective media
* within 90 days of receipt.
*
* Limitations on Rights to Redistribute This Code
*
* Unicode, Inc. hereby grants the right to freely use the information
* supplied in this file in the creation of products supporting the
* Unicode Standard, and to make copies of this file in any form
* for internal or external distribution as long as this notice
* remains attached.
*/
/* ---------------------------------------------------------------------
Conversions between UTF32, UTF-16, and UTF-8. Header file.
Several funtions are included here, forming a complete set of
conversions between the three formats. UTF-7 is not included
here, but is handled in a separate source file.
Each of these routines takes pointers to input buffers and output
buffers. The input buffers are const.
Each routine converts the text between *sourceStart and sourceEnd,
putting the result into the buffer between *targetStart and
targetEnd. Note: the end pointers are *after* the last item: e.g.
*(sourceEnd - 1) is the last item.
The return result indicates whether the conversion was successful,
and if not, whether the problem was in the source or target buffers.
(Only the first encountered problem is indicated.)
After the conversion, *sourceStart and *targetStart are both
updated to point to the end of last text successfully converted in
the respective buffers.
Input parameters:
sourceStart - pointer to a pointer to the source buffer.
The contents of this are modified on return so that
it points at the next thing to be converted.
targetStart - similarly, pointer to pointer to the target buffer.
sourceEnd, targetEnd - respectively pointers to the ends of the
two buffers, for overflow checking only.
These conversion functions take a ConversionFlags argument. When this
flag is set to strict, both irregular sequences and isolated surrogates
will cause an error. When the flag is set to lenient, both irregular
sequences and isolated surrogates are converted.
Whether the flag is strict or lenient, all illegal sequences will cause
an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>,
or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code
must check for illegal sequences.
When the flag is set to lenient, characters over 0x10FFFF are converted
to the replacement character; otherwise (when the flag is set to strict)
they constitute an error.
Output parameters:
The value "sourceIllegal" is returned from some routines if the input
sequence is malformed. When "sourceIllegal" is returned, the source
value will point to the illegal value that caused the problem. E.g.,
in UTF-8 when a sequence is malformed, it points to the start of the
malformed sequence.
Author: Mark E. Davis, 1994.
Rev History: Rick McGowan, fixes & updates May 2001.
Fixes & updates, Sept 2001.
------------------------------------------------------------------------ */
#ifndef _CONVERTUTF_H_
#define _CONVERTUTF_H_
/* ---------------------------------------------------------------------
The following 4 definitions are compiler-specific.
The C standard does not guarantee that wchar_t has at least
16 bits, so wchar_t is no less portable than unsigned short!
All should be unsigned values to avoid sign extension during
bit mask & shift operations.
------------------------------------------------------------------------ */
typedef unsigned long UTF32; /* at least 32 bits */
typedef unsigned short UTF16; /* at least 16 bits */
typedef unsigned char UTF8; /* typically 8 bits */
/* Some fundamental constants */
static const UTF32 UNI_REPLACEMENT_CHAR = 0x0000FFFD;
static const UTF32 UNI_MAX_BMP = 0x0000FFFF;
static const UTF32 UNI_MAX_UTF16 = 0x0010FFFF;
static const UTF32 UNI_MAX_UTF32 = 0x7FFFFFFF;
static const UTF32 UNI_MAX_LEGAL_UTF32 = 0x0010FFFF;
enum ConversionResult
{
conversionOK, /* conversion successful */
sourceExhausted, /* partial character in source, but hit end */
targetExhausted, /* insuff. room in target for conversion */
sourceIllegal /* source sequence is illegal/malformed */
};
enum ConversionFlags
{
strictConversion,
lenientConversion
};
ConversionResult ConvertUTF8toUTF16(const UTF8 **, const UTF8 *, UTF16 **, UTF16 *, ConversionFlags);
ConversionResult ConvertUTF16toUTF8(const UTF16 **, const UTF16 *, UTF8 **, UTF8 *, ConversionFlags);
ConversionResult ConvertUTF8toUTF32(const UTF8 **, const UTF8 *, UTF32 **, UTF32 *, ConversionFlags);
ConversionResult ConvertUTF32toUTF8(const UTF32 **, const UTF32 *, UTF8 **, UTF8 *, ConversionFlags);
ConversionResult ConvertUTF16toUTF32(const UTF16 **, const UTF16 *, UTF32 **, UTF32 *, ConversionFlags);
ConversionResult ConvertUTF32toUTF16(const UTF32 **, const UTF32 *, UTF16 **, UTF16 *, ConversionFlags);
bool isLegalUTF8Sequence(const UTF8 *, const UTF8 *);
/* --------------------------------------------------------------------- */
#endif

View File

@ -0,0 +1,40 @@
/*
* SSEQ Player - SDAT FAT (File Allocation Table) Section structures
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-03-21
*
* Nintendo DS Nitro Composer (SDAT) Specification document found at
* http://www.feshrine.net/hacking/doc/nds-sdat.html
*/
#include <stdexcept>
#include "FATSection.h"
FATRecord::FATRecord() : offset(0)
{
}
void FATRecord::Read(PseudoFile &file)
{
this->offset = file.ReadLE<uint32_t>();
file.ReadLE<uint32_t>(); // size
uint32_t reserved[2];
file.ReadLE(reserved);
}
FATSection::FATSection() : records()
{
}
void FATSection::Read(PseudoFile &file)
{
int8_t type[4];
file.ReadLE(type);
if (!VerifyHeader(type, "FAT "))
throw std::runtime_error("SDAT FAT Section invalid");
file.ReadLE<uint32_t>(); // size
uint32_t count = file.ReadLE<uint32_t>();
this->records.resize(count);
for (uint32_t i = 0; i < count; ++i)
this->records[i].Read(file);
}

View File

@ -0,0 +1,33 @@
/*
* SSEQ Player - SDAT FAT (File Allocation Table) Section structures
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-03-21
*
* Nintendo DS Nitro Composer (SDAT) Specification document found at
* http://www.feshrine.net/hacking/doc/nds-sdat.html
*/
#ifndef SSEQPLAYER_FATSECTION_H
#define SSEQPLAYER_FATSECTION_H
#include "common.h"
struct FATRecord
{
uint32_t offset;
FATRecord();
void Read(PseudoFile &file);
};
struct FATSection
{
std::vector<FATRecord> records;
FATSection();
void Read(PseudoFile &file);
};
#endif

View File

@ -0,0 +1,48 @@
/*
* SSEQ Player - SDAT INFO Entry structures
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-03-21
*
* Nintendo DS Nitro Composer (SDAT) Specification document found at
* http://www.feshrine.net/hacking/doc/nds-sdat.html
*/
#include "INFOEntry.h"
INFOEntrySEQ::INFOEntrySEQ() : fileID(0), bank(0), vol(0)
{
}
void INFOEntrySEQ::Read(PseudoFile &file)
{
this->fileID = file.ReadLE<uint16_t>();
file.ReadLE<uint16_t>(); // unknown
this->bank = file.ReadLE<uint16_t>();
this->vol = file.ReadLE<uint8_t>();
if (!this->vol)
this->vol = 0x7F; // Prevents nothing for volume
file.ReadLE<uint8_t>(); // cpr
file.ReadLE<uint8_t>(); // ppr
file.ReadLE<uint8_t>(); // ply
}
INFOEntryBANK::INFOEntryBANK() : fileID(0)
{
memset(this->waveArc, 0, sizeof(this->waveArc));
}
void INFOEntryBANK::Read(PseudoFile &file)
{
this->fileID = file.ReadLE<uint16_t>();
file.ReadLE<uint16_t>(); // unknown
file.ReadLE(this->waveArc);
}
INFOEntryWAVEARC::INFOEntryWAVEARC() : fileID(0)
{
}
void INFOEntryWAVEARC::Read(PseudoFile &file)
{
this->fileID = file.ReadLE<uint16_t>();
}

View File

@ -0,0 +1,54 @@
/*
* SSEQ Player - SDAT INFO Entry structures
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-03-21
*
* Nintendo DS Nitro Composer (SDAT) Specification document found at
* http://www.feshrine.net/hacking/doc/nds-sdat.html
*/
#ifndef SSEQPLAYER_INFOENTRY_H
#define SSEQPLAYER_INFOENTRY_H
#include "common.h"
struct INFOEntry
{
virtual ~INFOEntry()
{
}
virtual void Read(PseudoFile &file) = 0;
};
struct INFOEntrySEQ : INFOEntry
{
uint16_t fileID;
uint16_t bank;
uint8_t vol;
INFOEntrySEQ();
void Read(PseudoFile &file);
};
struct INFOEntryBANK : INFOEntry
{
uint16_t fileID;
uint16_t waveArc[4];
INFOEntryBANK();
void Read(PseudoFile &file);
};
struct INFOEntryWAVEARC : INFOEntry
{
uint16_t fileID;
INFOEntryWAVEARC();
void Read(PseudoFile &file);
};
#endif

View File

@ -0,0 +1,60 @@
/*
* SSEQ Player - SDAT INFO Section structures
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-03-25
*
* Nintendo DS Nitro Composer (SDAT) Specification document found at
* http://www.feshrine.net/hacking/doc/nds-sdat.html
*/
#include <vector>
#include "INFOSection.h"
template<typename T> INFORecord<T>::INFORecord() : entries()
{
}
template<typename T> void INFORecord<T>::Read(PseudoFile &file, uint32_t startOffset)
{
uint32_t count = file.ReadLE<uint32_t>();
auto entryOffsets = std::vector<uint32_t>(count);
file.ReadLE(entryOffsets);
for (uint32_t i = 0; i < count; ++i)
if (entryOffsets[i])
{
file.pos = startOffset + entryOffsets[i];
this->entries[i] = T();
this->entries[i].Read(file);
}
}
INFOSection::INFOSection() : SEQrecord(), BANKrecord(), WAVEARCrecord()
{
}
void INFOSection::Read(PseudoFile &file)
{
uint32_t startOfINFO = file.pos;
int8_t type[4];
file.ReadLE(type);
if (!VerifyHeader(type, "INFO"))
throw std::runtime_error("SDAT INFO Section invalid");
file.ReadLE<uint32_t>(); // size
uint32_t recordOffsets[8];
file.ReadLE(recordOffsets);
if (recordOffsets[REC_SEQ])
{
file.pos = startOfINFO + recordOffsets[REC_SEQ];
this->SEQrecord.Read(file, startOfINFO);
}
if (recordOffsets[REC_BANK])
{
file.pos = startOfINFO + recordOffsets[REC_BANK];
this->BANKrecord.Read(file, startOfINFO);
}
if (recordOffsets[REC_WAVEARC])
{
file.pos = startOfINFO + recordOffsets[REC_WAVEARC];
this->WAVEARCrecord.Read(file, startOfINFO);
}
}

View File

@ -0,0 +1,37 @@
/*
* SSEQ Player - SDAT INFO Section structures
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-03-21
*
* Nintendo DS Nitro Composer (SDAT) Specification document found at
* http://www.feshrine.net/hacking/doc/nds-sdat.html
*/
#ifndef SSEQPLAYER_INFOSECTION_H
#define SSEQPLAYER_INFOSECTION_H
#include <map>
#include "INFOEntry.h"
#include "common.h"
template<typename T> struct INFORecord
{
std::map<uint32_t, T> entries;
INFORecord();
void Read(PseudoFile &file, uint32_t startOffset);
};
struct INFOSection
{
INFORecord<INFOEntrySEQ> SEQrecord;
INFORecord<INFOEntryBANK> BANKrecord;
INFORecord<INFOEntryWAVEARC> WAVEARCrecord;
INFOSection();
void Read(PseudoFile &file);
};
#endif

View File

@ -0,0 +1,31 @@
/*
* SSEQ Player - Nintendo DS Standard Header structure
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-03-21
*
* Nintendo DS Nitro Composer (SDAT) Specification document found at
* http://www.feshrine.net/hacking/doc/nds-sdat.html
*/
#include <stdexcept>
#include "NDSStdHeader.h"
NDSStdHeader::NDSStdHeader() : magic(0)
{
memset(this->type, 0, sizeof(this->type));
}
void NDSStdHeader::Read(PseudoFile &file)
{
file.ReadLE(this->type);
this->magic = file.ReadLE<uint32_t>();
file.ReadLE<uint32_t>(); // file size
file.ReadLE<uint16_t>(); // structure size
file.ReadLE<uint16_t>(); // # of blocks
}
void NDSStdHeader::Verify(const std::string &typeToCheck, uint32_t magicToCheck)
{
if (!VerifyHeader(this->type, typeToCheck) || this->magic != magicToCheck)
throw std::runtime_error("NDS Standard Header for " + typeToCheck + " invalid");
}

View File

@ -0,0 +1,26 @@
/*
* SSEQ Player - Nintendo DS Standard Header structure
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-03-21
*
* Nintendo DS Nitro Composer (SDAT) Specification document found at
* http://www.feshrine.net/hacking/doc/nds-sdat.html
*/
#ifndef SSEQPLAYER_NDSSTDHEADER_H
#define SSEQPLAYER_NDSSTDHEADER_H
#include "common.h"
struct NDSStdHeader
{
int8_t type[4];
uint32_t magic;
NDSStdHeader();
void Read(PseudoFile &file);
void Verify(const std::string &typeToCheck, uint32_t magicToCheck);
};
#endif

View File

@ -0,0 +1,240 @@
/*
* SSEQ Player - Player structure
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-04-01
*
* Adapted from source code of FeOS Sound System
* By fincs
* https://github.com/fincs/FSS
*/
#include "Player.h"
#include "common.h"
Player::Player() : prio(0), nTracks(0), tempo(0), tempoCount(0), tempoRate(0), masterVol(0), sseq(nullptr), sampleRate(0), interpolation(INTERPOLATION_NONE)
{
memset(this->trackIds, 0, sizeof(this->trackIds));
for (size_t i = 0; i < 16; ++i)
this->channels[i].chnId = i;
}
bool Player::Setup(const SSEQ *sseqToPlay)
{
this->sseq = sseqToPlay;
int firstTrack = this->TrackAlloc();
if (firstTrack == -1)
return false;
this->tracks[firstTrack].Init(firstTrack, this, nullptr, 0);
this->nTracks = 1;
this->trackIds[0] = firstTrack;
auto pData = &this->sseq->data[0];
if (*pData == 0xFE)
for (pData += 3; *pData == 0x93; ) // Prepare extra tracks
{
++pData;
int tNum = read8(&pData);
auto pos = &this->sseq->data[read24(&pData)];
int newTrack = this->TrackAlloc();
if (newTrack == -1)
continue;
this->tracks[newTrack].Init(newTrack, this, pos, tNum);
this->trackIds[this->nTracks++] = newTrack;
}
this->tracks[firstTrack].startPos = this->tracks[firstTrack].pos = pData;
this->secondsPerSample = 1.0 / this->sampleRate;
this->ClearState();
return true;
}
void Player::ClearState()
{
this->tempo = 120;
this->tempoCount = 0;
this->tempoRate = 0x100;
this->masterVol = 0; // this is actually the highest level
this->secondsIntoPlayback = 0;
this->secondsUntilNextClock = SecondsPerClockCycle;
}
void Player::FreeTracks()
{
for (uint8_t i = 0; i < this->nTracks; ++i)
this->tracks[this->trackIds[i]].Free();
this->nTracks = 0;
}
void Player::Stop(bool bKillSound)
{
this->ClearState();
for (uint8_t i = 0; i < this->nTracks; ++i)
{
uint8_t trackId = this->trackIds[i];
this->tracks[trackId].ClearState();
for (int j = 0; j < 16; ++j)
{
Channel &chn = this->channels[j];
if (chn.state != CS_NONE && chn.trackId == trackId)
{
if (bKillSound)
chn.Kill();
else
chn.Release();
}
}
}
this->FreeTracks();
}
int Player::ChannelAlloc(int type, int priority)
{
static const uint8_t pcmChnArray[] = { 4, 5, 6, 7, 2, 0, 3, 1, 8, 9, 10, 11, 14, 12, 15, 13 };
static const uint8_t psgChnArray[] = { 13, 12, 11, 10, 9, 8 };
static const uint8_t noiseChnArray[] = { 15, 14 };
static const uint8_t arraySizes[] = { sizeof(pcmChnArray), sizeof(psgChnArray), sizeof(noiseChnArray) };
static const uint8_t *const arrayArray[] = { pcmChnArray, psgChnArray, noiseChnArray };
auto chnArray = arrayArray[type];
int arraySize = arraySizes[type];
int curChnNo = -1;
for (int i = 0; i < arraySize; ++i)
{
int thisChnNo = chnArray[i];
Channel &thisChn = this->channels[thisChnNo];
Channel &curChn = this->channels[curChnNo];
if (curChnNo != -1 && thisChn.prio >= curChn.prio)
{
if (thisChn.prio != curChn.prio)
continue;
if (curChn.vol <= thisChn.vol)
continue;
}
curChnNo = thisChnNo;
}
if (curChnNo == -1 || priority < this->channels[curChnNo].prio)
return -1;
this->channels[curChnNo].ply = this;
this->channels[curChnNo].noteLength = -1;
this->channels[curChnNo].vol = 0;
this->channels[curChnNo].clearHistory();
return curChnNo;
}
int Player::TrackAlloc()
{
for (int i = 0; i < FSS_MAXTRACKS; ++i)
{
Track &thisTrk = this->tracks[i];
if (!thisTrk.state[TS_ALLOCBIT])
{
thisTrk.Zero();
thisTrk.state.set(TS_ALLOCBIT);
thisTrk.updateFlags.reset();
return i;
}
}
return -1;
}
void Player::Run()
{
while (this->tempoCount > 240)
{
this->tempoCount -= 240;
for (uint8_t i = 0; i < this->nTracks; ++i)
this->tracks[this->trackIds[i]].Run();
}
this->tempoCount += (static_cast<int>(this->tempo) * static_cast<int>(this->tempoRate)) >> 8;
}
void Player::UpdateTracks()
{
for (int i = 0; i < 16; ++i)
this->channels[i].UpdateTrack();
for (int i = 0; i < FSS_MAXTRACKS; ++i)
this->tracks[i].updateFlags.reset();
}
void Player::Timer()
{
this->UpdateTracks();
for (int i = 0; i < 16; ++i)
this->channels[i].Update();
this->Run();
}
template<typename T1, typename T2> static inline void clamp(T1 &valueToClamp, const T2 &minValue, const T2 &maxValue)
{
if (valueToClamp < minValue)
valueToClamp = minValue;
else if (valueToClamp > maxValue)
valueToClamp = maxValue;
}
static inline int32_t muldiv7(int32_t val, uint8_t mul)
{
return mul == 127 ? val : ((val * mul) >> 7);
}
void Player::GenerateSamples(std::vector<uint8_t> &buf, unsigned offset, unsigned samples)
{
unsigned long mute = this->mutes.to_ulong();
for (unsigned smpl = 0; smpl < samples; ++smpl)
{
this->secondsIntoPlayback += this->secondsPerSample;
int32_t leftChannel = 0, rightChannel = 0;
// I need to advance the sound channels here
for (int i = 0; i < 16; ++i)
{
Channel &chn = this->channels[i];
if (chn.state > CS_NONE)
{
int32_t sample = chn.GenerateSample();
chn.IncrementSample();
if (mute & BIT(i))
continue;
uint8_t datashift = chn.reg.volumeDiv;
if (datashift == 3)
datashift = 4;
sample = muldiv7(sample, chn.reg.volumeMul) >> datashift;
leftChannel += muldiv7(sample, 127 - chn.reg.panning);
rightChannel += muldiv7(sample, chn.reg.panning);
}
}
leftChannel = muldiv7(leftChannel, 127 - this->masterVol);
rightChannel = muldiv7(rightChannel, 127 - this->masterVol);
clamp(leftChannel, -0x8000, 0x7FFF);
clamp(rightChannel, -0x8000, 0x7FFF);
buf[offset++] = leftChannel & 0xFF;
buf[offset++] = (leftChannel >> 8) & 0xFF;
buf[offset++] = rightChannel & 0xFF;
buf[offset++] = (rightChannel >> 8) & 0xFF;
if (this->secondsIntoPlayback > this->secondsUntilNextClock)
{
this->Timer();
this->secondsUntilNextClock += SecondsPerClockCycle;
}
}
}

View File

@ -0,0 +1,54 @@
/*
* SSEQ Player - Player structure
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-04-01
*
* Adapted from source code of FeOS Sound System
* By fincs
* https://github.com/fincs/FSS
*/
#ifndef SSEQPLAYER_PLAYER_H
#define SSEQPLAYER_PLAYER_H
#include <memory>
#include <bitset>
#include "SSEQ.h"
#include "Track.h"
#include "Channel.h"
#include "consts.h"
struct Player
{
uint8_t prio, nTracks;
uint16_t tempo, tempoCount, tempoRate /* 8.8 fixed point */;
int16_t masterVol;
const SSEQ *sseq;
uint8_t trackIds[FSS_TRACKCOUNT];
Track tracks[FSS_MAXTRACKS];
Channel channels[16];
uint32_t sampleRate;
Interpolation interpolation;
Player();
bool Setup(const SSEQ *sseq);
void ClearState();
void FreeTracks();
void Stop(bool bKillSound);
int ChannelAlloc(int type, int prio);
int TrackAlloc();
void Run();
void UpdateTracks();
void Timer();
/* Playback helper */
double secondsPerSample, secondsIntoPlayback, secondsUntilNextClock;
std::bitset<16> mutes;
void GenerateSamples(std::vector<uint8_t> &buf, unsigned offset, unsigned samples);
};
#endif

View File

@ -0,0 +1,122 @@
/*
* SSEQ Player - SDAT SBNK (Sound Bank) structures
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-03-25
*
* Nintendo DS Nitro Composer (SDAT) Specification document found at
* http://www.feshrine.net/hacking/doc/nds-sdat.html
*/
#include "SBNK.h"
#include "NDSStdHeader.h"
SBNKInstrumentRange::SBNKInstrumentRange(uint8_t lowerNote, uint8_t upperNote, int recordType) : lowNote(lowerNote), highNote(upperNote),
record(recordType), swav(0), swar(0), noteNumber(0), attackRate(0), decayRate(0), sustainLevel(0), releaseRate(0), pan(0)
{
}
void SBNKInstrumentRange::Read(PseudoFile &file)
{
this->swav = file.ReadLE<uint16_t>();
this->swar = file.ReadLE<uint16_t>();
this->noteNumber = file.ReadLE<uint8_t>();
this->attackRate = file.ReadLE<uint8_t>();
this->decayRate = file.ReadLE<uint8_t>();
this->sustainLevel = file.ReadLE<uint8_t>();
this->releaseRate = file.ReadLE<uint8_t>();
this->pan = file.ReadLE<uint8_t>();
}
SBNKInstrument::SBNKInstrument() : record(0), ranges()
{
}
void SBNKInstrument::Read(PseudoFile &file, uint32_t startOffset)
{
this->record = file.ReadLE<uint8_t>();
uint16_t offset = file.ReadLE<uint16_t>();
file.ReadLE<uint8_t>();
uint32_t endOfInst = file.pos;
file.pos = startOffset + offset;
if (this->record)
{
if (this->record == 16)
{
uint8_t lowNote = file.ReadLE<uint8_t>();
uint8_t highNote = file.ReadLE<uint8_t>();
uint8_t num = highNote - lowNote + 1;
for (uint8_t i = 0; i < num; ++i)
{
uint16_t thisRecord = file.ReadLE<uint16_t>();
auto range = SBNKInstrumentRange(lowNote + i, lowNote + i, thisRecord);
range.Read(file);
this->ranges.push_back(range);
}
}
else if (this->record == 17)
{
uint8_t thisRanges[8];
file.ReadLE(thisRanges);
uint8_t i = 0;
while (i < 8 && thisRanges[i])
{
uint16_t thisRecord = file.ReadLE<uint16_t>();
uint8_t lowNote = i ? thisRanges[i - 1] + 1 : 0;
uint8_t highNote = thisRanges[i];
auto range = SBNKInstrumentRange(lowNote, highNote, thisRecord);
range.Read(file);
this->ranges.push_back(range);
++i;
}
}
else
{
auto range = SBNKInstrumentRange(0, 127, this->record);
range.Read(file);
this->ranges.push_back(range);
}
}
file.pos = endOfInst;
}
SBNK::SBNK(const std::string &fn) : filename(fn), instruments(), info()
{
memset(this->waveArc, 0, sizeof(this->waveArc));
}
SBNK::SBNK(const SBNK &sbnk) : filename(sbnk.filename), instruments(sbnk.instruments), info(sbnk.info)
{
memcpy(this->waveArc, sbnk.waveArc, sizeof(this->waveArc));
}
SBNK &SBNK::operator=(const SBNK &sbnk)
{
if (this != &sbnk)
{
this->filename = sbnk.filename;
this->instruments = sbnk.instruments;
memcpy(this->waveArc, sbnk.waveArc, sizeof(this->waveArc));
this->info = sbnk.info;
}
return *this;
}
void SBNK::Read(PseudoFile &file)
{
uint32_t startOfSBNK = file.pos;
NDSStdHeader header;
header.Read(file);
header.Verify("SBNK", 0x0100FEFF);
int8_t type[4];
file.ReadLE(type);
if (!VerifyHeader(type, "DATA"))
throw std::runtime_error("SBNK DATA structure invalid");
file.ReadLE<uint32_t>(); // size
uint32_t reserved[8];
file.ReadLE(reserved);
uint32_t count = file.ReadLE<uint32_t>();
this->instruments.resize(count);
for (uint32_t i = 0; i < count; ++i)
this->instruments[i].Read(file, startOfSBNK);
}

View File

@ -0,0 +1,61 @@
/*
* SSEQ Player - SDAT SBNK (Sound Bank) structures
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-03-21
*
* Nintendo DS Nitro Composer (SDAT) Specification document found at
* http://www.feshrine.net/hacking/doc/nds-sdat.html
*/
#ifndef SSEQPLAYER_SBNK_H
#define SSEQPLAYER_SBNK_H
#include "SWAR.h"
#include "INFOEntry.h"
#include "common.h"
struct SBNKInstrumentRange
{
uint8_t lowNote;
uint8_t highNote;
uint16_t record;
uint16_t swav;
uint16_t swar;
uint8_t noteNumber;
uint8_t attackRate;
uint8_t decayRate;
uint8_t sustainLevel;
uint8_t releaseRate;
uint8_t pan;
SBNKInstrumentRange(uint8_t lowerNote, uint8_t upperNote, int recordType);
void Read(PseudoFile &file);
};
struct SBNKInstrument
{
uint8_t record;
std::vector<SBNKInstrumentRange> ranges;
SBNKInstrument();
void Read(PseudoFile &file, uint32_t startOffset);
};
struct SBNK
{
std::string filename;
std::vector<SBNKInstrument> instruments;
const SWAR *waveArc[4];
INFOEntryBANK info;
SBNK(const std::string &fn = "");
SBNK(const SBNK &sbnk);
SBNK &operator=(const SBNK &sbnk);
void Read(PseudoFile &file);
};
#endif

View File

@ -0,0 +1,93 @@
/*
* SSEQ Player - SDAT structure
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-03-21
*
* Nintendo DS Nitro Composer (SDAT) Specification document found at
* http://www.feshrine.net/hacking/doc/nds-sdat.html
*/
#include "SDAT.h"
#include "NDSStdHeader.h"
#include "SYMBSection.h"
#include "INFOSection.h"
#include "FATSection.h"
#include "convert.h"
SDAT::SDAT(PseudoFile &file, uint32_t sseqToLoad) : sseq(), sbnk()
{
// Read sections
NDSStdHeader header;
header.Read(file);
header.Verify("SDAT", 0x0100FEFF);
uint32_t SYMBOffset = file.ReadLE<uint32_t>();
file.ReadLE<uint32_t>(); // SYMB size
uint32_t INFOOffset = file.ReadLE<uint32_t>();
file.ReadLE<uint32_t>(); // INFO size
uint32_t FATOffset = file.ReadLE<uint32_t>();
file.ReadLE<uint32_t>(); // FAT Size
SYMBSection symbSection;
if (SYMBOffset)
{
file.pos = SYMBOffset;
symbSection.Read(file);
}
file.pos = INFOOffset;
INFOSection infoSection;
infoSection.Read(file);
file.pos = FATOffset;
FATSection fatSection;
fatSection.Read(file);
if (infoSection.SEQrecord.entries.empty())
throw std::logic_error("No SSEQ records found in SDAT");
if (!infoSection.SEQrecord.entries.count(sseqToLoad))
throw std::range_error("SSEQ of " + stringify(sseqToLoad) + " is not found");
// Read SSEQ
if (infoSection.SEQrecord.entries.count(sseqToLoad))
{
uint16_t fileID = infoSection.SEQrecord.entries[sseqToLoad].fileID;
std::string name = "SSEQ" + NumToHexString(fileID).substr(2);
if (SYMBOffset)
name = NumToHexString(sseqToLoad).substr(6) + " - " + symbSection.SEQrecord.entries[sseqToLoad];
file.pos = fatSection.records[fileID].offset;
SSEQ *newSSEQ = new SSEQ(name);
newSSEQ->info = infoSection.SEQrecord.entries[sseqToLoad];
newSSEQ->Read(file);
this->sseq.reset(newSSEQ);
// Read SBNK for this SSEQ
uint16_t bank = newSSEQ->info.bank;
fileID = infoSection.BANKrecord.entries[bank].fileID;
name = "SBNK" + NumToHexString(fileID).substr(2);
if (SYMBOffset)
name = NumToHexString(bank).substr(2) + " - " + symbSection.BANKrecord.entries[bank];
file.pos = fatSection.records[fileID].offset;
SBNK *newSBNK = new SBNK(name);
newSSEQ->bank = newSBNK;
newSBNK->info = infoSection.BANKrecord.entries[bank];
newSBNK->Read(file);
this->sbnk.reset(newSBNK);
// Read SWARs for this SBNK
for (int i = 0; i < 4; ++i)
if (newSBNK->info.waveArc[i] != 0xFFFF)
{
uint16_t waveArc = newSBNK->info.waveArc[i];
fileID = infoSection.WAVEARCrecord.entries[waveArc].fileID;
name = "SWAR" + NumToHexString(fileID).substr(2);
if (SYMBOffset)
name = NumToHexString(waveArc).substr(2) + " - " + symbSection.WAVEARCrecord.entries[waveArc];
file.pos = fatSection.records[fileID].offset;
SWAR *newSWAR = new SWAR(name);
newSBNK->waveArc[i] = newSWAR;
newSWAR->info = infoSection.WAVEARCrecord.entries[waveArc];
newSWAR->Read(file);
this->swar[i].reset(newSWAR);
}
else
this->swar[i].release();
}
}

View File

@ -0,0 +1,31 @@
/*
* SSEQ Player - SDAT structure
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-03-30
*
* Nintendo DS Nitro Composer (SDAT) Specification document found at
* http://www.feshrine.net/hacking/doc/nds-sdat.html
*/
#ifndef SSEQPLAYER_SDAT_H
#define SSEQPLAYER_SDAT_H
#include <memory>
#include "SSEQ.h"
#include "SBNK.h"
#include "SWAR.h"
#include "common.h"
struct SDAT
{
std::unique_ptr<SSEQ> sseq;
std::unique_ptr<SBNK> sbnk;
std::unique_ptr<SWAR> swar[4];
SDAT(PseudoFile &file, uint32_t sseqToLoad);
private:
SDAT(const SDAT &);
SDAT &operator=(const SDAT &);
};
#endif

View File

@ -0,0 +1,49 @@
/*
* SSEQ Player - SDAT SSEQ (Sequence) structure
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-03-30
*
* Nintendo DS Nitro Composer (SDAT) Specification document found at
* http://www.feshrine.net/hacking/doc/nds-sdat.html
*/
#include "SSEQ.h"
#include "NDSStdHeader.h"
SSEQ::SSEQ(const std::string &fn) : filename(fn), data(), bank(nullptr), info()
{
}
SSEQ::SSEQ(const SSEQ &sseq) : filename(sseq.filename), data(sseq.data), bank(sseq.bank), info(sseq.info)
{
}
SSEQ &SSEQ::operator=(const SSEQ &sseq)
{
if (this != &sseq)
{
this->filename = sseq.filename;
this->data = sseq.data;
this->bank = sseq.bank;
this->info = sseq.info;
}
return *this;
}
void SSEQ::Read(PseudoFile &file)
{
uint32_t startOfSSEQ = file.pos;
NDSStdHeader header;
header.Read(file);
header.Verify("SSEQ", 0x0100FEFF);
int8_t type[4];
file.ReadLE(type);
if (!VerifyHeader(type, "DATA"))
throw std::runtime_error("SSEQ DATA structure invalid");
uint32_t size = file.ReadLE<uint32_t>();
uint32_t dataOffset = file.ReadLE<uint32_t>();
this->data.resize(size - 12, 0);
file.pos = startOfSSEQ + dataOffset;
file.ReadLE(this->data);
}

View File

@ -0,0 +1,32 @@
/*
* SSEQ Player - SDAT SSEQ (Sequence) structure
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-03-21
*
* Nintendo DS Nitro Composer (SDAT) Specification document found at
* http://www.feshrine.net/hacking/doc/nds-sdat.html
*/
#ifndef SSEQPLAYER_SSEQ_H
#define SSEQPLAYER_SSEQ_H
#include "SBNK.h"
#include "INFOEntry.h"
#include "common.h"
struct SSEQ
{
std::string filename;
std::vector<uint8_t> data;
const SBNK *bank;
INFOEntrySEQ info;
SSEQ(const std::string &fn = "");
SSEQ(const SSEQ &sseq);
SSEQ &operator=(const SSEQ &sseq);
void Read(PseudoFile &file);
};
#endif

View File

@ -0,0 +1,30 @@
<?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>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>org.nowork.sseqplayer</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>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2013 Christopher Snowhill. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@ -0,0 +1,41 @@
/*
* SSEQ Player - SDAT SWAR (Wave Archive) structures
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-03-25
*
* Nintendo DS Nitro Composer (SDAT) Specification document found at
* http://www.feshrine.net/hacking/doc/nds-sdat.html
*/
#include <vector>
#include "SWAR.h"
#include "NDSStdHeader.h"
SWAR::SWAR(const std::string &fn) : filename(fn), swavs(), info()
{
}
void SWAR::Read(PseudoFile &file)
{
uint32_t startOfSWAR = file.pos;
NDSStdHeader header;
header.Read(file);
header.Verify("SWAR", 0x0100FEFF);
int8_t type[4];
file.ReadLE(type);
if (!VerifyHeader(type, "DATA"))
throw std::runtime_error("SWAR DATA structure invalid");
file.ReadLE<uint32_t>(); // size
uint32_t reserved[8];
file.ReadLE(reserved);
uint32_t count = file.ReadLE<uint32_t>();
auto offsets = std::vector<uint32_t>(count);
file.ReadLE(offsets);
for (uint32_t i = 0; i < count; ++i)
if (offsets[i])
{
file.pos = startOfSWAR + offsets[i];
this->swavs[i] = SWAV();
this->swavs[i].Read(file);
}
}

View File

@ -0,0 +1,33 @@
/*
* SSEQ Player - SDAT SWAR (Wave Archive) structures
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-03-21
*
* Nintendo DS Nitro Composer (SDAT) Specification document found at
* http://www.feshrine.net/hacking/doc/nds-sdat.html
*/
#ifndef SSEQPLAYER_SWAR_H
#define SSEQPLAYER_SWAR_H
#include <map>
#include "SWAV.h"
#include "INFOEntry.h"
#include "common.h"
/*
* The size has been left out of this structure as it is unused by this player.
*/
struct SWAR
{
std::string filename;
std::map<uint32_t, SWAV> swavs;
INFOEntryWAVEARC info;
SWAR(const std::string &fn = "");
void Read(PseudoFile &file);
};
#endif

View File

@ -0,0 +1,123 @@
/*
* SSEQ Player - SDAT SWAV (Waveform/Sample) structure
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-04-10
*
* Nintendo DS Nitro Composer (SDAT) Specification document found at
* http://www.feshrine.net/hacking/doc/nds-sdat.html
*/
#include "SWAV.h"
static int ima_index_table[] =
{
-1, -1, -1, -1, 2, 4, 6, 8,
-1, -1, -1, -1, 2, 4, 6, 8
};
static int ima_step_table[] =
{
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
};
SWAV::SWAV() : waveType(0), loop(0), sampleRate(0), time(0), loopOffset(0), nonLoopLength(0), data(), dataptr(nullptr)
{
}
static inline void DecodeADPCMNibble(int32_t nibble, int32_t &stepIndex, int32_t &predictedValue)
{
int32_t step = ima_step_table[stepIndex];
stepIndex += ima_index_table[nibble];
if (stepIndex < 0)
stepIndex = 0;
else if (stepIndex > 88)
stepIndex = 88;
int32_t diff = step >> 3;
if (nibble & 4)
diff += step;
if (nibble & 2)
diff += step >> 1;
if (nibble & 1)
diff += step >> 2;
if (nibble & 8)
predictedValue -= diff;
else
predictedValue += diff;
if (predictedValue < -0x8000)
predictedValue = -0x8000;
else if (predictedValue > 0x7FFF)
predictedValue = 0x7FFF;
}
void SWAV::DecodeADPCM(const std::vector<uint8_t> &origData)
{
int32_t predictedValue = origData[0] | (origData[1] << 8);
int32_t stepIndex = origData[2] | (origData[3] << 8);
for (int i = 0, len = origData.size() - 4; i < len; ++i)
{
int32_t nibble = origData[i + 4] & 0x0F;
DecodeADPCMNibble(nibble, stepIndex, predictedValue);
this->data[2 * i] = predictedValue;
nibble = (origData[i + 4] >> 4) & 0x0F;
DecodeADPCMNibble(nibble, stepIndex, predictedValue);
this->data[2 * i + 1] = predictedValue;
}
}
void SWAV::Read(PseudoFile &file)
{
this->waveType = file.ReadLE<uint8_t>();
this->loop = file.ReadLE<uint8_t>();
this->sampleRate = file.ReadLE<uint16_t>();
this->time = file.ReadLE<uint16_t>();
this->loopOffset = file.ReadLE<uint16_t>();
this->nonLoopLength = file.ReadLE<uint32_t>();
uint32_t size = (this->loopOffset + this->nonLoopLength) * 4;
auto origData = std::vector<uint8_t>(size);
file.ReadLE(origData);
// Convert data accordingly
if (!this->waveType)
{
// PCM 8-bit -> PCM signed 16-bit
this->data.resize(origData.size(), 0);
for (size_t i = 0, len = origData.size(); i < len; ++i)
this->data[i] = origData[i] << 8;
this->loopOffset *= 4;
this->nonLoopLength *= 4;
}
else if (this->waveType == 1)
{
// PCM signed 16-bit, no conversion
this->data.resize(origData.size() / 2, 0);
for (size_t i = 0, len = origData.size() / 2; i < len; ++i)
this->data[i] = ReadLE<int16_t>(&origData[2 * i]);
this->loopOffset *= 2;
this->nonLoopLength *= 2;
}
else if (this->waveType == 2)
{
// IMA ADPCM -> PCM signed 16-bit
this->data.resize((origData.size() - 4) * 2, 0);
this->DecodeADPCM(origData);
--this->loopOffset;
this->loopOffset *= 8;
this->nonLoopLength *= 8;
}
this->dataptr = &this->data[0];
}

View File

@ -0,0 +1,32 @@
/*
* SSEQ Player - SDAT SWAV (Waveform/Sample) structure
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-04-10
*
* Nintendo DS Nitro Composer (SDAT) Specification document found at
* http://www.feshrine.net/hacking/doc/nds-sdat.html
*/
#ifndef SSEQPLAYER_SWAV_H
#define SSEQPLAYER_SWAV_H
#include "common.h"
struct SWAV
{
uint8_t waveType;
uint8_t loop;
uint16_t sampleRate;
uint16_t time;
uint32_t loopOffset;
uint32_t nonLoopLength;
std::vector<int16_t> data;
const int16_t *dataptr;
SWAV();
void Read(PseudoFile &file);
void DecodeADPCM(const std::vector<uint8_t> &data);
};
#endif

View File

@ -0,0 +1,59 @@
/*
* SSEQ Player - SDAT SYMB (Symbol/Filename) Section structures
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-03-25
*
* Nintendo DS Nitro Composer (SDAT) Specification document found at
* http://www.feshrine.net/hacking/doc/nds-sdat.html
*/
#include <vector>
#include "SYMBSection.h"
SYMBRecord::SYMBRecord() : entries()
{
}
void SYMBRecord::Read(PseudoFile &file, uint32_t startOffset)
{
uint32_t count = file.ReadLE<uint32_t>();
auto entryOffsets = std::vector<uint32_t>(count);
file.ReadLE(entryOffsets);
for (uint32_t i = 0; i < count; ++i)
if (entryOffsets[i])
{
file.pos = startOffset + entryOffsets[i];
this->entries[i] = file.ReadNullTerminatedString();
}
}
SYMBSection::SYMBSection() : SEQrecord(), BANKrecord(), WAVEARCrecord()
{
}
void SYMBSection::Read(PseudoFile &file)
{
uint32_t startOfSYMB = file.pos;
int8_t type[4];
file.ReadLE(type);
if (!VerifyHeader(type, "SYMB"))
throw std::runtime_error("SDAT SYMB Section invalid");
file.ReadLE<uint32_t>(); // size
uint32_t recordOffsets[8];
file.ReadLE(recordOffsets);
if (recordOffsets[REC_SEQ])
{
file.pos = startOfSYMB + recordOffsets[REC_SEQ];
this->SEQrecord.Read(file, startOfSYMB);
}
if (recordOffsets[REC_BANK])
{
file.pos = startOfSYMB + recordOffsets[REC_BANK];
this->BANKrecord.Read(file, startOfSYMB);
}
if (recordOffsets[REC_WAVEARC])
{
file.pos = startOfSYMB + recordOffsets[REC_WAVEARC];
this->WAVEARCrecord.Read(file, startOfSYMB);
}
}

View File

@ -0,0 +1,39 @@
/*
* SSEQ Player - SDAT SYMB (Symbol/Filename) Section structures
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-03-21
*
* Nintendo DS Nitro Composer (SDAT) Specification document found at
* http://www.feshrine.net/hacking/doc/nds-sdat.html
*/
#ifndef SSEQPLAYER_SYMBSECTION_H
#define SSEQPLAYER_SYMBSECTION_H
#include <map>
#include "common.h"
struct SYMBRecord
{
std::map<uint32_t, std::string> entries;
SYMBRecord();
void Read(PseudoFile &file, uint32_t startOffset);
};
/*
* The size has been left out of this structure as it is unused by this player.
*/
struct SYMBSection
{
SYMBRecord SEQrecord;
SYMBRecord BANKrecord;
SYMBRecord WAVEARCrecord;
SYMBSection();
void Read(PseudoFile &file);
};
#endif

View File

@ -0,0 +1,552 @@
/*
* SSEQ Player - Track structure
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-04-01
*
* Adapted from source code of FeOS Sound System
* By fincs
* https://github.com/fincs/FSS
*/
#include "Track.h"
#include "Player.h"
#include "common.h"
Track::Track()
{
this->Zero();
}
void Track::Init(uint8_t handle, Player *player, const uint8_t *dataPos, int n)
{
this->trackId = handle;
this->num = n;
this->ply = player;
this->startPos = dataPos;
this->ClearState();
}
void Track::Zero()
{
this->trackId = -1;
this->state.reset();
this->num = this->prio = 0;
this->ply = nullptr;
this->startPos = this->pos = nullptr;
memset(this->stack, 0, sizeof(this->stack));
this->stackPos = 0;
memset(this->loopCount, 0, sizeof(this->loopCount));
this->wait = 0;
this->patch = 0;
this->portaKey = this->portaTime = 0;
this->sweepPitch = 0;
this->vol = this->expr = 0;
this->pan = 0;
this->pitchBendRange = 0;
this->pitchBend = this->transpose = 0;
this->a = this->d = this->s = this->r = 0;
this->modType = this->modSpeed = this->modDepth = this->modRange = 0;
this->modDelay = 0;
this->updateFlags.reset();
}
void Track::ClearState()
{
this->state.reset();
this->state.set(TS_ALLOCBIT);
this->state.set(TS_NOTEWAIT);
this->prio = this->ply->prio + 64;
this->pos = this->startPos;
this->stackPos = 0;
this->wait = 0;
this->patch = 0;
this->portaKey = 60;
this->portaTime = 0;
this->sweepPitch = 0;
this->vol = 64;
this->expr = 127;
this->pan = 0;
this->pitchBendRange = 2;
this->pitchBend = this->transpose = 0;
this->a = this->d = this->s = this->r = 0xFF;
this->modType = 0;
this->modRange = 1;
this->modSpeed = 16;
this->modDelay = 10;
this->modDepth = 0;
}
void Track::Free()
{
this->state.reset();
this->updateFlags.reset();
}
int Track::NoteOn(int key, int vel, int len)
{
auto sbnk = this->ply->sseq->bank;
if (this->patch >= sbnk->instruments.size())
return -1;
bool bIsPCM = true;
Channel *chn = nullptr;
int nCh = -1;
auto &instrument = sbnk->instruments[this->patch];
const SBNKInstrumentRange *noteDef = nullptr;
int fRecord = instrument.record;
if (fRecord == 16)
{
if (!(instrument.ranges[0].lowNote <= key && key <= instrument.ranges[instrument.ranges.size() - 1].highNote))
return -1;
int rn = key - instrument.ranges[0].lowNote;
noteDef = &instrument.ranges[rn];
fRecord = noteDef->record;
}
else if (fRecord == 17)
{
size_t reg, ranges;
for (reg = 0, ranges = instrument.ranges.size(); reg < ranges; ++reg)
if (key <= instrument.ranges[reg].highNote)
break;
if (reg == ranges)
return -1;
noteDef = &instrument.ranges[reg];
fRecord = noteDef->record;
}
if (!fRecord)
return -1;
else if (fRecord == 1)
{
if (!noteDef)
noteDef = &instrument.ranges[0];
}
else if (fRecord < 4)
{
// PSG
// fRecord = 2 -> PSG tone, pNoteDef->wavid -> PSG duty
// fRecord = 3 -> PSG noise
bIsPCM = false;
if (!noteDef)
noteDef = &instrument.ranges[0];
if (fRecord == 3)
{
nCh = this->ply->ChannelAlloc(TYPE_NOISE, this->prio);
if (nCh < 0)
return -1;
chn = &this->ply->channels[nCh];
chn->tempReg.CR = SOUND_FORMAT_PSG | SCHANNEL_ENABLE;
}
else
{
nCh = this->ply->ChannelAlloc(TYPE_PSG, this->prio);
if (nCh < 0)
return -1;
chn = &this->ply->channels[nCh];
chn->tempReg.CR = SOUND_FORMAT_PSG | SCHANNEL_ENABLE | SOUND_DUTY(noteDef->swav & 0x7);
}
// TODO: figure out what pNoteDef->tnote means for PSG channels
chn->tempReg.TIMER = -SOUND_FREQ(440 * 8); // key #69 (A4)
chn->reg.samplePosition = -1;
chn->reg.psgX = 0x7FFF;
}
if (bIsPCM)
{
nCh = this->ply->ChannelAlloc(TYPE_PCM, this->prio);
if (nCh < 0)
return -1;
chn = &this->ply->channels[nCh];
auto swav = &sbnk->waveArc[noteDef->swar]->swavs.find(noteDef->swav)->second;
chn->tempReg.CR = SOUND_FORMAT(swav->waveType & 3) | SOUND_LOOP(!!swav->loop) | SCHANNEL_ENABLE;
chn->tempReg.SOURCE = swav;
chn->tempReg.TIMER = swav->time;
chn->tempReg.REPEAT_POINT = swav->loopOffset;
chn->tempReg.LENGTH = swav->nonLoopLength;
chn->reg.samplePosition = -3;
}
chn->state = CS_START;
chn->trackId = this->trackId;
chn->flags.reset();
chn->prio = this->prio;
chn->key = key;
chn->orgKey = bIsPCM ? noteDef->noteNumber : 69;
chn->velocity = Cnv_Sust(vel);
chn->pan = static_cast<int>(noteDef->pan) - 64;
chn->modDelayCnt = 0;
chn->modCounter = 0;
chn->noteLength = len;
chn->reg.sampleIncrease = 0;
chn->attackLvl = Cnv_Attack(this->a == 0xFF ? noteDef->attackRate : this->a);
chn->decayRate = Cnv_Fall(this->d == 0xFF ? noteDef->decayRate : this->d);
chn->sustainLvl = this->s == 0xFF ? noteDef->sustainLevel : this->s;
chn->releaseRate = Cnv_Fall(this->r == 0xFF ? noteDef->releaseRate : this->r);
chn->UpdateVol(*this);
chn->UpdatePan(*this);
chn->UpdateTune(*this);
chn->UpdateMod(*this);
chn->UpdatePorta(*this);
this->portaKey = key;
return nCh;
}
int Track::NoteOnTie(int key, int vel)
{
// Find an existing note
int i;
Channel *chn = nullptr;
for (i = 0; i < 16; ++i)
{
chn = &this->ply->channels[i];
if (chn->state > CS_NONE && chn->trackId == this->trackId && chn->state != CS_RELEASE)
break;
}
if (i == 16)
// Can't find note -> create an endless one
return this->NoteOn(key, vel, -1);
chn->flags.reset();
chn->prio = this->prio;
chn->key = key;
chn->velocity = Cnv_Sust(vel);
chn->modDelayCnt = 0;
chn->modCounter = 0;
chn->UpdateVol(*this);
//chn->UpdatePan(*this);
chn->UpdateTune(*this);
chn->UpdateMod(*this);
chn->UpdatePorta(*this);
this->portaKey = key;
chn->flags.set(CF_UPDTMR);
return i;
}
void Track::ReleaseAllNotes()
{
for (int i = 0; i < 16; ++i)
{
Channel &chn = this->ply->channels[i];
if (chn.state > CS_NONE && chn.trackId == this->trackId && chn.state != CS_RELEASE)
chn.Release();
}
}
enum SseqCommand
{
SSEQ_CMD_REST = 0x80,
SSEQ_CMD_PATCH = 0x81,
SSEQ_CMD_PAN = 0xC0,
SSEQ_CMD_VOL = 0xC1,
SSEQ_CMD_MASTERVOL = 0xC2,
SSEQ_CMD_PRIO = 0xC6,
SSEQ_CMD_NOTEWAIT = 0xC7,
SSEQ_CMD_TIE = 0xC8,
SSEQ_CMD_EXPR = 0xD5,
SSEQ_CMD_TEMPO = 0xE1,
SSEQ_CMD_END = 0xFF,
SSEQ_CMD_GOTO = 0x94,
SSEQ_CMD_CALL = 0x95,
SSEQ_CMD_RET = 0xFD,
SSEQ_CMD_LOOPSTART = 0xD4,
SSEQ_CMD_LOOPEND = 0xFC,
SSEQ_CMD_TRANSPOSE = 0xC3,
SSEQ_CMD_PITCHBEND = 0xC4,
SSEQ_CMD_PITCHBENDRANGE = 0xC5,
SSEQ_CMD_ATTACK = 0xD0,
SSEQ_CMD_DECAY = 0xD1,
SSEQ_CMD_SUSTAIN = 0xD2,
SSEQ_CMD_RELEASE = 0xD3,
SSEQ_CMD_PORTAKEY = 0xC9,
SSEQ_CMD_PORTAFLAG = 0xCE,
SSEQ_CMD_PORTATIME = 0xCF,
SSEQ_CMD_SWEEPPITCH = 0xE3,
SSEQ_CMD_MODDEPTH = 0xCA,
SSEQ_CMD_MODSPEED = 0xCB,
SSEQ_CMD_MODTYPE = 0xCC,
SSEQ_CMD_MODRANGE = 0xCD,
SSEQ_CMD_MODDELAY = 0xE0,
SSEQ_CMD_RANDOM = 0xA0,
SSEQ_CMD_PRINTVAR = 0xD6,
SSEQ_CMD_IF = 0xA2,
SSEQ_CMD_UNSUP1 = 0xA1,
SSEQ_CMD_UNSUP2_LO = 0xB0,
SSEQ_CMD_UNSUP2_HI = 0xBD
};
void Track::Run()
{
// Indicate "heartbeat" for this track
this->updateFlags.set(TUF_LEN);
// Exit if the track has already ended
if (this->state[TS_END])
return;
if (this->wait)
{
--this->wait;
if (this->wait)
return;
}
auto pData = &this->pos;
while (!this->wait)
{
int cmd = read8(pData);
if (cmd < 0x80)
{
// Note on
int key = cmd + this->transpose;
int vel = read8(pData);
int len = readvl(pData);
if (this->state[TS_NOTEWAIT])
this->wait = len;
if (this->state[TS_TIEBIT])
this->NoteOnTie(key, vel);
else
this->NoteOn(key, vel, len);
}
else
switch (cmd)
{
//-----------------------------------------------------------------
// Main commands
//-----------------------------------------------------------------
case SSEQ_CMD_REST:
this->wait = readvl(pData);
break;
case SSEQ_CMD_PATCH:
this->patch = readvl(pData);
break;
case SSEQ_CMD_GOTO:
*pData = &this->ply->sseq->data[read24(pData)];
break;
case SSEQ_CMD_CALL:
{
const uint8_t *dest = &this->ply->sseq->data[read24(pData)];
this->stack[this->stackPos++] = *pData;
*pData = dest;
break;
}
case SSEQ_CMD_RET:
*pData = this->stack[--this->stackPos];
break;
case SSEQ_CMD_PAN:
this->pan = read8(pData) - 64;
this->updateFlags.set(TUF_PAN);
break;
case SSEQ_CMD_VOL:
this->vol = read8(pData);
this->updateFlags.set(TUF_VOL);
break;
case SSEQ_CMD_MASTERVOL:
this->ply->masterVol = Cnv_Sust(read8(pData));
for (uint8_t i = 0; i < this->ply->nTracks; ++i)
this->ply->tracks[this->ply->trackIds[i]].updateFlags.set(TUF_VOL);
break;
case SSEQ_CMD_PRIO:
this->prio = this->ply->prio + read8(pData);
// Update here?
break;
case SSEQ_CMD_NOTEWAIT:
this->state.set(TS_NOTEWAIT, !!read8(pData));
break;
case SSEQ_CMD_TIE:
this->state.set(TS_TIEBIT, !!read8(pData));
this->ReleaseAllNotes();
break;
case SSEQ_CMD_EXPR:
this->expr = read8(pData);
this->updateFlags.set(TUF_VOL);
break;
case SSEQ_CMD_TEMPO:
this->ply->tempo = read16(pData);
break;
case SSEQ_CMD_END:
this->state.set(TS_END);
return;
case SSEQ_CMD_LOOPSTART:
this->loopCount[this->stackPos] = read8(pData);
this->stack[this->stackPos++] = *pData;
break;
case SSEQ_CMD_LOOPEND:
if (this->stackPos)
{
const uint8_t *rPos = this->stack[this->stackPos - 1];
uint8_t &nR = this->loopCount[this->stackPos - 1];
uint8_t prevR = nR;
if (prevR && !--nR)
--this->stackPos;
*pData = rPos;
}
break;
//-----------------------------------------------------------------
// Tuning commands
//-----------------------------------------------------------------
case SSEQ_CMD_TRANSPOSE:
this->transpose = read8(pData);
break;
case SSEQ_CMD_PITCHBEND:
this->pitchBend = read8(pData);
this->updateFlags.set(TUF_TIMER);
break;
case SSEQ_CMD_PITCHBENDRANGE:
this->pitchBendRange = read8(pData);
this->updateFlags.set(TUF_TIMER);
break;
//-----------------------------------------------------------------
// Envelope-related commands
//-----------------------------------------------------------------
case SSEQ_CMD_ATTACK:
this->a = read8(pData);
break;
case SSEQ_CMD_DECAY:
this->d = read8(pData);
break;
case SSEQ_CMD_SUSTAIN:
this->s = read8(pData);
break;
case SSEQ_CMD_RELEASE:
this->r = read8(pData);
break;
//-----------------------------------------------------------------
// Portamento-related commands
//-----------------------------------------------------------------
case SSEQ_CMD_PORTAKEY:
this->portaKey = read8(pData) + this->transpose;
this->state.set(TS_PORTABIT);
// Update here?
break;
case SSEQ_CMD_PORTAFLAG:
this->state.set(TS_PORTABIT, !!read8(pData));
// Update here?
break;
case SSEQ_CMD_PORTATIME:
this->portaTime = read8(pData);
// Update here?
break;
case SSEQ_CMD_SWEEPPITCH:
this->sweepPitch = read16(pData);
// Update here?
break;
//-----------------------------------------------------------------
// Modulation-related commands
//-----------------------------------------------------------------
case SSEQ_CMD_MODDEPTH:
this->modDepth = read8(pData);
this->updateFlags.set(TUF_MOD);
break;
case SSEQ_CMD_MODSPEED:
this->modSpeed = read8(pData);
this->updateFlags.set(TUF_MOD);
break;
case SSEQ_CMD_MODTYPE:
this->modType = read8(pData);
this->updateFlags.set(TUF_MOD);
break;
case SSEQ_CMD_MODRANGE:
this->modRange = read8(pData);
this->updateFlags.set(TUF_MOD);
break;
case SSEQ_CMD_MODDELAY:
this->modDelay = read16(pData);
this->updateFlags.set(TUF_MOD);
break;
//-----------------------------------------------------------------
// Variable-related commands
//-----------------------------------------------------------------
case SSEQ_CMD_RANDOM: // TODO
*pData += 5;
break;
case SSEQ_CMD_PRINTVAR: // TODO
*pData += 1;
break;
case SSEQ_CMD_UNSUP1: // TODO
{
int t = read8(pData);
if (t >= SSEQ_CMD_UNSUP2_LO && t <= SSEQ_CMD_UNSUP2_HI)
*pData += 1;
*pData += 1;
break;
}
case SSEQ_CMD_IF: // TODO
break;
default:
if (cmd >= SSEQ_CMD_UNSUP2_LO && cmd <= SSEQ_CMD_UNSUP2_HI) // TODO
*pData += 3;
}
}
}

View File

@ -0,0 +1,62 @@
/*
* SSEQ Player - Track structure
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-04-01
*
* Adapted from source code of FeOS Sound System
* By fincs
* https://github.com/fincs/FSS
*/
#ifndef SSEQPLAYER_TRACK_H
#define SSEQPLAYER_TRACK_H
#include <bitset>
#include "consts.h"
struct Player;
struct Track
{
int8_t trackId;
std::bitset<TS_BITS> state;
uint8_t num, prio;
Player *ply;
const uint8_t *startPos;
const uint8_t *pos;
const uint8_t *stack[FSS_TRACKSTACKSIZE];
uint8_t stackPos;
uint8_t loopCount[FSS_TRACKSTACKSIZE];
int wait;
uint16_t patch;
uint8_t portaKey, portaTime;
int16_t sweepPitch;
uint8_t vol, expr;
int8_t pan; // -64..63
uint8_t pitchBendRange;
int8_t pitchBend;
int8_t transpose;
uint8_t a, d, s, r;
uint8_t modType, modSpeed, modDepth, modRange;
uint16_t modDelay;
std::bitset<TUF_BITS> updateFlags;
Track();
void Init(uint8_t handle, Player *ply, const uint8_t *pos, int n);
void Zero();
void ClearState();
void Free();
int NoteOn(int key, int vel, int len);
int NoteOnTie(int key, int vel);
void ReleaseAllNotes();
void Run();
};
#endif

View File

@ -0,0 +1,29 @@
// original code is from here: http://www.codeproject.com/Tips/197097/Converting-ANSI-to-Unicode-and-back
// License: The Code Project Open License (CPOL) http://www.codeproject.com/info/cpol10.aspx
#include "UTFEncodeDecode.h"
#include "UtfConverter.h"
std::string EncodeToUTF8(const std::string &source, const std::locale &L)
{
try
{
return UtfConverter::ToUtf8(cp_converter<>(L).widen(source));
}
catch (const std::runtime_error &)
{
return "";
}
}
std::string DecodeFromUTF8(const std::string &source, const std::locale &L)
{
try
{
return cp_converter<>(L).narrow(UtfConverter::FromUtf8(source));
}
catch (const std::runtime_error &)
{
return "";
}
}

View File

@ -0,0 +1,63 @@
// original code is from here: http://www.codeproject.com/Tips/197097/Converting-ANSI-to-Unicode-and-back
// License: The Code Project Open License (CPOL) http://www.codeproject.com/info/cpol10.aspx
#ifndef UTFENCODEDECODE_H
#define UTFENCODEDECODE_H
#include <string>
#include <locale>
#include <sstream>
#include <stdexcept>
std::string EncodeToUTF8(const std::string &, const std::locale & = std::locale::classic());
std::string DecodeFromUTF8(const std::string &, const std::locale & = std::locale::classic());
template<size_t buf_size = 100> class cp_converter
{
const std::locale loc;
public:
cp_converter(const std::locale &L = std::locale::classic()) : loc(L) { }
inline std::wstring widen(const std::string &in)
{
return this->convert<char, wchar_t>(in);
}
inline std::string narrow(const std::wstring &in)
{
return this->convert<wchar_t, char>(in);
}
private:
typedef std::codecvt<wchar_t, char, mbstate_t> codecvt_facet;
// widen
inline codecvt_facet::result cv(const codecvt_facet &facet, mbstate_t &s, const char *f1, const char *l1, const char *&n1, wchar_t *f2, wchar_t *l2, wchar_t *&n2) const
{
return facet.in(s, f1, l1, n1, f2, l2, n2);
}
// narrow
inline codecvt_facet::result cv(const codecvt_facet &facet, mbstate_t &s, const wchar_t *f1, const wchar_t *l1, const wchar_t *&n1, char *f2, char *l2, char *&n2) const
{
return facet.out(s, f1, l1, n1, f2, l2, n2);
}
template<class ct_in, class ct_out> std::basic_string<ct_out> convert(const std::basic_string<ct_in> &in)
{
auto &facet = std::use_facet<codecvt_facet>(this->loc);
std::basic_stringstream<ct_out> os;
ct_out buf[buf_size];
mbstate_t state = {0};
codecvt_facet::result result;
const ct_in *ipc = &in[0];
do
{
ct_out *opc = nullptr;
result = this->cv(facet, state, ipc, &in[0] + in.length(), ipc, buf, buf + buf_size, opc);
os << std::basic_string<ct_out>(buf, opc - buf);
} while (ipc < &in[0] + in.length() && result != codecvt_facet::error);
if (result != codecvt_facet::ok)
throw std::runtime_error("result is not ok!");
return os.str();
}
};
#endif

View File

@ -0,0 +1,69 @@
// original code is from here: http://www.codeproject.com/Articles/17573/Convert-Between-std-string-and-std-wstring-UTF-8-a
// License: The Code Project Open License (CPOL) http://www.codeproject.com/info/cpol10.aspx
#include <string>
#include <vector>
#include <stdexcept>
#include "ConvertUTF.h"
namespace UtfConverter
{
std::wstring FromUtf8(const std::string &utf8string)
{
auto widesize = utf8string.length();
auto result = std::vector<wchar_t>(widesize + 1, L'\0');
auto orig = std::vector<char>(widesize + 1, '\0');
std::copy(utf8string.begin(), utf8string.end(), orig.begin());
auto *sourcestart = reinterpret_cast<const UTF8 *>(&orig[0]), *sourceend = sourcestart + widesize;
ConversionResult res;
if (sizeof(wchar_t) == 2)
{
auto *targetstart = reinterpret_cast<UTF16 *>(&result[0]), *targetend = targetstart + widesize;
res = ConvertUTF8toUTF16(&sourcestart, sourceend, &targetstart, targetend, strictConversion);
*targetstart = 0;
unsigned end = targetstart - reinterpret_cast<UTF16 *>(&result[0]);
result.erase(result.begin() + end, result.end());
}
else if (sizeof(wchar_t) == 4)
{
auto *targetstart = reinterpret_cast<UTF32 *>(&result[0]), *targetend = targetstart + widesize;
res = ConvertUTF8toUTF32(&sourcestart, sourceend, &targetstart, targetend, strictConversion);
*targetstart = 0;
unsigned end = targetstart - reinterpret_cast<UTF32 *>(&result[0]);
result.erase(result.begin() + end, result.end());
}
else
throw std::runtime_error("UtfConverter::FromUtf8: sizeof(wchar_t) is not 2 or 4.");
if (res != conversionOK)
throw std::runtime_error("UtfConverter::FromUtf8: Conversion failed.");
return std::wstring(result.begin(), result.end());
}
std::string ToUtf8(const std::wstring &widestring)
{
auto widesize = widestring.length(), utf8size = (sizeof(wchar_t) == 2 ? 3 : 4) * widesize + 1;
auto result = std::vector<char>(utf8size, '\0');
auto orig = std::vector<wchar_t>(widesize + 1, L'\0');
std::copy(widestring.begin(), widestring.end(), orig.begin());
auto *targetstart = reinterpret_cast<UTF8 *>(&result[0]), *targetend = targetstart + utf8size;
ConversionResult res;
if (sizeof(wchar_t) == 2)
{
auto *sourcestart = reinterpret_cast<const UTF16 *>(&orig[0]), *sourceend = sourcestart + widesize;
res = ConvertUTF16toUTF8(&sourcestart, sourceend, &targetstart, targetend, strictConversion);
}
else if (sizeof(wchar_t) == 4)
{
auto *sourcestart = reinterpret_cast<const UTF32 *>(&orig[0]), *sourceend = sourcestart + widesize;
res = ConvertUTF32toUTF8(&sourcestart, sourceend, &targetstart, targetend, strictConversion);
}
else
throw std::runtime_error("UtfConverter::ToUtf8: sizeof(wchar_t) is not 2 or 4.");
if (res != conversionOK)
throw std::runtime_error("UtfConverter::ToUtf8: Conversion failed.");
*targetstart = 0;
auto end = targetstart - reinterpret_cast<UTF8 *>(&result[0]);
result.erase(result.begin() + end, result.end());
return std::string(result.begin(), result.end());
}
}

View File

@ -0,0 +1,15 @@
// original code is from here: http://www.codeproject.com/Articles/17573/Convert-Between-std-string-and-std-wstring-UTF-8-a
// License: The Code Project Open License (CPOL) http://www.codeproject.com/info/cpol10.aspx
#ifndef UTFCONVERTER_H
#define UTFCONVERTER_H
#include <string>
namespace UtfConverter
{
std::wstring FromUtf8(const std::string &);
std::string ToUtf8(const std::wstring &);
}
#endif

View File

@ -0,0 +1,241 @@
/*
* SSEQ Player - Common functions
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-03-3
*
* Some code from FeOS Sound System
* By fincs
* https://github.com/fincs/FSS
*/
#ifndef SSEQPLAYER_COMMON_H
#define SSEQPLAYER_COMMON_H
#include <string>
#include <vector>
#include <cstring>
#include <cstdint>
/*
* Pseudo-file data structure
*/
struct PseudoFile
{
std::vector<uint8_t> *data;
uint32_t pos;
PseudoFile() : data(nullptr), pos(0)
{
}
template<typename T> T ReadLE()
{
T finalVal = 0;
for (size_t i = 0; i < sizeof(T); ++i)
finalVal |= (*this->data)[this->pos++] << (i * 8);
return finalVal;
}
template<typename T, size_t N> void ReadLE(T (&arr)[N])
{
for (size_t i = 0; i < N; ++i)
arr[i] = this->ReadLE<T>();
}
template<size_t N> void ReadLE( uint8_t arr[N])
{
memcpy(&arr[0], &(*this->data)[this->pos], N);
this->pos += N;
}
template<typename T> void ReadLE(std::vector<T> &arr)
{
for (size_t i = 0, len = arr.size(); i < len; ++i)
arr[i] = this->ReadLE<T>();
}
void ReadLE(std::vector<uint8_t> &arr)
{
memcpy(&arr[0], &(*this->data)[this->pos], arr.size());
this->pos += arr.size();
}
std::string ReadNullTerminatedString()
{
char chr;
std::string str;
do
{
chr = static_cast<char>(this->ReadLE<uint8_t>());
if (chr)
str += chr;
} while (chr);
return str;
}
};
/*
* Data Reading
*
* The following ReadLE functions will either read from a file or from an
* array (sent in as a pointer), while making sure that the data is read in
* as little-endian formating.
*/
template<typename T> inline T ReadLE(const uint8_t *arr)
{
T finalVal = 0;
for (size_t i = 0; i < sizeof(T); ++i)
finalVal |= arr[i] << (i * 8);
return finalVal;
}
/*
* The following function is used to convert an integer into a hexidecimal
* string, the length being determined by the size of the integer. 8-bit
* integers are in the format of 0x00, 16-bit integers are in the format of
* 0x0000, and so on.
*/
template<typename T> inline std::string NumToHexString(const T &num)
{
std::string hex;
uint8_t len = sizeof(T) * 2;
for (uint8_t i = 0; i < len; ++i)
{
uint8_t tmp = (num >> (i * 4)) & 0xF;
hex = static_cast<char>(tmp < 10 ? tmp + '0' : tmp - 10 + 'a') + hex;
}
return "0x" + hex;
}
/*
* SDAT Record types
* List of types taken from the Nitro Composer Specification
* http://www.feshrine.net/hacking/doc/nds-sdat.html
*/
enum RecordName
{
REC_SEQ,
REC_SEQARC,
REC_BANK,
REC_WAVEARC,
REC_PLAYER,
REC_GROUP,
REC_PLAYER2,
REC_STRM
};
template<size_t N> inline bool VerifyHeader(int8_t (&arr)[N], const std::string &header)
{
std::string arrHeader = std::string(&arr[0], &arr[N]);
return arrHeader == header;
}
/*
* The remaining functions in this file come from the FeOS Sound System source code.
*/
inline int Cnv_Attack(int attk)
{
static const uint8_t lut[] =
{
0x00, 0x01, 0x05, 0x0E, 0x1A, 0x26, 0x33, 0x3F, 0x49, 0x54,
0x5C, 0x64, 0x6D, 0x74, 0x7B, 0x7F, 0x84, 0x89, 0x8F
};
return attk >= 0x6D ? lut[0x7F - attk] : 0xFF - attk;
}
inline int Cnv_Fall(int fall)
{
if (fall == 0x7F)
return 0xFFFF;
else if (fall == 0x7E)
return 0x3C00;
else if (fall < 0x32)
return ((fall << 1) + 1) & 0xFFFF;
else
return (0x1E00 / (0x7E - fall)) & 0xFFFF;
}
inline int Cnv_Sust(int sust)
{
static const int16_t lut[] =
{
-32768, -722, -721, -651, -601, -562, -530, -503,
-480, -460, -442, -425, -410, -396, -383, -371,
-360, -349, -339, -330, -321, -313, -305, -297,
-289, -282, -276, -269, -263, -257, -251, -245,
-239, -234, -229, -224, -219, -214, -210, -205,
-201, -196, -192, -188, -184, -180, -176, -173,
-169, -165, -162, -158, -155, -152, -149, -145,
-142, -139, -136, -133, -130, -127, -125, -122,
-119, -116, -114, -111, -109, -106, -103, -101,
-99, -96, -94, -91, -89, -87, -85, -82,
-80, -78, -76, -74, -72, -70, -68, -66,
-64, -62, -60, -58, -56, -54, -52, -50,
-49, -47, -45, -43, -42, -40, -38, -36,
-35, -33, -31, -30, -28, -27, -25, -23,
-22, -20, -19, -17, -16, -14, -13, -11,
-10, -8, -7, -6, -4, -3, -1, 0
};
return lut[sust];
}
inline int Cnv_Sine(int arg)
{
static const int lut_size = 32;
static const int8_t lut[] =
{
0, 6, 12, 19, 25, 31, 37, 43, 49, 54, 60, 65, 71, 76, 81, 85, 90, 94,
98, 102, 106, 109, 112, 115, 117, 120, 122, 123, 125, 126, 126, 127, 127
};
if (arg < lut_size)
return lut[arg];
if (arg < 2 * lut_size)
return lut[2 * lut_size - arg];
if (arg < 3 * lut_size)
return -lut[arg - 2 * lut_size];
/*else*/
return -lut[4 * lut_size - arg];
}
inline int read8(const uint8_t **ppData)
{
auto pData = *ppData;
int x = *pData;
*ppData = pData + 1;
return x;
}
inline int read16(const uint8_t **ppData)
{
int x = read8(ppData);
x |= read8(ppData) << 8;
return x;
}
inline int read24(const uint8_t **ppData)
{
int x = read8(ppData);
x |= read8(ppData) << 8;
x |= read8(ppData) << 16;
return x;
}
inline int readvl(const uint8_t **ppData)
{
int x = 0;
for (;;)
{
int data = read8(ppData);
x = (x << 7) | (data & 0x7F);
if (!(data & 0x80))
break;
}
return x;
}
#endif

View File

@ -0,0 +1,65 @@
/*
* SSEQ Player - Constants/Macros
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-04-12
*
* Adapted from source code of FeOS Sound System
* By fincs
* https://github.com/fincs/FSS
*
* Some constants/macros also taken from libdns, part of the devkitARM portion of devkitPro
* http://devkitpro.org/
*/
#ifndef SSEQPLAYER_CONSTS_H
#define SSEQPLAYER_CONSTS_H
#include <cstdint>
const uint32_t ARM7_CLOCK = 33513982;
const double SecondsPerClockCycle = 64.0 * 2728.0 / ARM7_CLOCK;
inline uint32_t BIT(uint32_t n) { return 1 << n; }
enum { TS_ALLOCBIT, TS_NOTEWAIT, TS_PORTABIT, TS_TIEBIT, TS_END, TS_BITS };
enum { TUF_VOL, TUF_PAN, TUF_TIMER, TUF_MOD, TUF_LEN, TUF_BITS };
enum { CS_NONE, CS_START, CS_ATTACK, CS_DECAY, CS_SUSTAIN, CS_RELEASE };
enum { CF_UPDVOL, CF_UPDPAN, CF_UPDTMR, CF_BITS };
enum { TYPE_PCM, TYPE_PSG, TYPE_NOISE };
const int FSS_TRACKCOUNT = 16;
const int FSS_MAXTRACKS = 32;
const int FSS_TRACKSTACKSIZE = 3;
const int AMPL_K = 723;
const int AMPL_MIN = -AMPL_K;
const int AMPL_THRESHOLD = AMPL_MIN << 7;
inline int SOUND_FREQ(int n) { return -0x1000000 / n; }
inline uint32_t SOUND_VOL(int n) { return n; }
inline uint32_t SOUND_VOLDIV(int n) { return n << 8; }
inline uint32_t SOUND_PAN(int n) { return n << 16; }
inline uint32_t SOUND_DUTY(int n) { return n << 24; }
const uint32_t SOUND_REPEAT = BIT(27);
const uint32_t SOUND_ONE_SHOT = BIT(28);
inline uint32_t SOUND_LOOP(bool a) { return a ? SOUND_REPEAT : SOUND_ONE_SHOT; }
const uint32_t SOUND_FORMAT_PSG = 3 << 29;
inline uint32_t SOUND_FORMAT(int n) { return n << 29; }
const uint32_t SCHANNEL_ENABLE = BIT(31);
enum Interpolation
{
INTERPOLATION_NONE,
INTERPOLATION_LINEAR,
INTERPOLATION_COSINE,
INTERPOLATION_4POINTBSPLINE,
INTERPOLATION_6POINTOSCULATING,
INTERPOLATION_6POINTBSPLINE,
INTERPOLATION_LANCZOS
};
#endif

View File

@ -0,0 +1,156 @@
/*
* Common conversion functions
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
* Last modification on 2013-03-30
*/
#ifndef _CONVERT_H_
#define _CONVERT_H_
#include <stdexcept>
#include <string>
#include <sstream>
#include <typeinfo>
#include <locale>
#include <vector>
#include <cmath>
#include "BigSString.h"
/*
* The following exception class and the *stringify and convert* functions are
* from the C++ FAQ, section 39, entry 3:
* http://www.parashift.com/c++-faq/convert-string-to-any.html
*
* The convert and convertTo functions were made into templates of std::basic_string
* to handle wide-character strings properly, as well as adding wstringify for the
* same reason.
*/
class BadConversion : public std::runtime_error
{
public:
BadConversion(const std::string &s) : std::runtime_error(s) { }
};
template<typename T> inline std::string stringify(const T &x)
{
std::ostringstream o;
if (!(o << x))
throw BadConversion(std::string("stringify(") + typeid(x).name() + ")");
return o.str();
}
template<typename T> inline std::wstring wstringify(const T &x)
{
std::wostringstream o;
if (!(o << x))
throw BadConversion(std::string("wstringify(") + typeid(x).name() + ")");
return o.str();
}
template<typename T, typename S> inline void convert(const std::basic_string<S> &s, T &x, bool failIfLeftoverChars = true)
{
std::basic_istringstream<S> i(s);
S c;
if (!(i >> x) || (failIfLeftoverChars && i.get(c)))
throw BadConversion(std::string("convert(") + typeid(S).name() + ")");
}
template<typename T, typename S> inline T convertTo(const std::basic_string<S> &s, bool failIfLeftoverChars = true)
{
T x;
convert(s, x, failIfLeftoverChars);
return x;
}
// Miscellaneous conversion functions
class ConvertFuncs
{
private:
static inline bool IsDigitsOnly(const std::string &input, const std::locale &loc = std::locale::classic())
{
auto inputChars = std::vector<char>(input.begin(), input.end());
size_t length = inputChars.size();
auto masks = std::vector<std::ctype<char>::mask>(length);
std::use_facet<std::ctype<char>>(loc).is(&inputChars[0], &inputChars[length], &masks[0]);
for (size_t x = 0; x < length; ++x)
if (inputChars[x] != '.' && !(masks[x] & std::ctype<char>::digit))
return false;
return true;
}
public:
static unsigned long StringToMS(const std::string &time)
{
unsigned long hours = 0, minutes = 0;
double seconds = 0.0;
std::string hoursStr, minutesStr, secondsStr;
size_t firstcolon = time.find(':');
if (firstcolon != std::string::npos)
{
size_t secondcolon = time.substr(firstcolon + 1).find(':');
if (secondcolon != std::string::npos)
{
secondcolon = firstcolon + secondcolon + 1;
hoursStr = time.substr(0, firstcolon);
minutesStr = time.substr(firstcolon + 1, secondcolon - firstcolon - 1);
secondsStr = time.substr(secondcolon + 1);
}
else
{
minutesStr = time.substr(0, firstcolon);
secondsStr = time.substr(firstcolon + 1);
}
}
else
secondsStr = time;
if (!hoursStr.empty())
{
if (!ConvertFuncs::IsDigitsOnly(hoursStr))
return 0;
hours = convertTo<unsigned long>(hoursStr, false);
}
if (!minutesStr.empty())
{
if (!ConvertFuncs::IsDigitsOnly(minutesStr))
return 0;
minutes = convertTo<unsigned long>(minutesStr, false);
}
if (!secondsStr.empty())
{
if (!ConvertFuncs::IsDigitsOnly(secondsStr))
return 0;
size_t comma = secondsStr.find(',');
if (comma != std::string::npos)
secondsStr[comma] = '.';
seconds = convertTo<double>(secondsStr, false);
}
seconds += minutes * 60 + hours * 1440;
return static_cast<unsigned long>(std::floor(seconds * 1000 + 0.5));
}
static unsigned long StringToMS(const std::wstring &time)
{
return ConvertFuncs::StringToMS(String(time).GetAnsi());
}
static std::string MSToString(unsigned long time)
{
double seconds = time / 1000.0;
if (seconds < 60)
return stringify(seconds);
unsigned long minutes = static_cast<unsigned long>(seconds) / 60;
seconds -= minutes * 60;
if (minutes < 60)
return stringify(minutes) + ":" + (seconds < 10 ? "0" : "") + stringify(seconds);
unsigned long hours = minutes / 60;
minutes %= 60;
return stringify(hours) + ":" + (minutes < 10 ? "0" : "") + stringify(minutes) + ":" + (seconds < 10 ? "0" : "") + stringify(seconds);
}
static std::wstring MSToWString(unsigned long time)
{
return String(ConvertFuncs::MSToString(time)).GetWStr();
}
};
#endif

View File

@ -0,0 +1,2 @@
/* Localized versions of Info.plist keys */

View File

@ -217,7 +217,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.6;
MACOSX_DEPLOYMENT_TARGET = 10.7;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
@ -249,7 +249,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.6;
MACOSX_DEPLOYMENT_TARGET = 10.7;
SDKROOT = macosx;
};
name = Release;

View File

@ -9,7 +9,7 @@
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>NoWork-Inc.${PRODUCT_NAME:rfc1034identifier}</string>
<string>org.nowork.psflib</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>

View File

@ -24,6 +24,21 @@
<key>CFBundleTypeRole</key>
<string>None</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>minincsf</string>
<string>ncsf</string>
</array>
<key>LSTypeIsPackage</key>
<false/>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleTypeIconFile</key>
<string>vg.icns</string>
<key>CFBundleTypeName</key>
<string>Nitro Sound Composer Audio File</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>

View File

@ -19,6 +19,8 @@
8360EEE817F92AC8005208A4 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8360EEE717F92AC8005208A4 /* Cocoa.framework */; };
8360EEF217F92AC8005208A4 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8360EEF017F92AC8005208A4 /* InfoPlist.strings */; };
8360EF6E17F92E86005208A4 /* HighlyExperimental.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8360EF4417F92C92005208A4 /* HighlyExperimental.framework */; };
8384904A180764B500E7332D /* SSEQPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83848FEC1807624000E7332D /* SSEQPlayer.framework */; };
8384904B180764C200E7332D /* SSEQPlayer.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83848FEC1807624000E7332D /* SSEQPlayer.framework */; };
83F18C3917F9301400471B6C /* HighlyExperimental.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8360EF4417F92C92005208A4 /* HighlyExperimental.framework */; };
/* End PBXBuildFile section */
@ -93,6 +95,20 @@
remoteGlobalIDString = 8360EF1017F92C91005208A4;
remoteInfo = HighlyExperimental;
};
83848FEB1807624000E7332D /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 83848FE61807623F00E7332D /* SSEQPlayer.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 83848FB81807623F00E7332D;
remoteInfo = SSEQPlayer;
};
83849048180764AC00E7332D /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 83848FE61807623F00E7332D /* SSEQPlayer.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = 83848FB71807623F00E7332D;
remoteInfo = SSEQPlayer;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
@ -102,6 +118,7 @@
dstPath = "";
dstSubfolderSpec = 10;
files = (
8384904B180764C200E7332D /* SSEQPlayer.framework in CopyFiles */,
834379A617F97EB000584396 /* HighlyAdvanced.framework in CopyFiles */,
8343792A17F96F2600584396 /* HighlyQuixotic.framework in CopyFiles */,
834378D317F9677300584396 /* HighlyTheoretical.framework in CopyFiles */,
@ -130,6 +147,7 @@
8360EEF117F92AC8005208A4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
8360EEF317F92AC8005208A4 /* HighlyComplete-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "HighlyComplete-Prefix.pch"; sourceTree = "<group>"; };
8360EF3E17F92C91005208A4 /* HighlyExperimental.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = HighlyExperimental.xcodeproj; path = ../../Frameworks/HighlyExperimental/HighlyExperimental.xcodeproj; sourceTree = "<group>"; };
83848FE61807623F00E7332D /* SSEQPlayer.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SSEQPlayer.xcodeproj; path = ../../Frameworks/SSEQPlayer/SSEQPlayer.xcodeproj; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -137,6 +155,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
8384904A180764B500E7332D /* SSEQPlayer.framework in Frameworks */,
834379A517F97EA100584396 /* HighlyAdvanced.framework in Frameworks */,
8343792917F96F1D00584396 /* HighlyQuixotic.framework in Frameworks */,
834378D217F9676600584396 /* HighlyTheoretical.framework in Frameworks */,
@ -208,6 +227,7 @@
8343789C17F9658E00584396 /* HighlyTheoretical.xcodeproj */,
8343790C17F96E2600584396 /* HighlyQuixotic.xcodeproj */,
8343796317F97BDB00584396 /* HighlyAdvanced.xcodeproj */,
83848FE61807623F00E7332D /* SSEQPlayer.xcodeproj */,
);
name = Frameworks;
sourceTree = "<group>";
@ -252,6 +272,14 @@
name = Products;
sourceTree = "<group>";
};
83848FE71807623F00E7332D /* Products */ = {
isa = PBXGroup;
children = (
83848FEC1807624000E7332D /* SSEQPlayer.framework */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -267,6 +295,7 @@
buildRules = (
);
dependencies = (
83849049180764AC00E7332D /* PBXTargetDependency */,
834379A417F97E9C00584396 /* PBXTargetDependency */,
8343792817F96F1900584396 /* PBXTargetDependency */,
834378D117F9675E00584396 /* PBXTargetDependency */,
@ -318,6 +347,10 @@
ProductGroup = 8343784B17F93CB500584396 /* Products */;
ProjectRef = 8343784A17F93CB500584396 /* psflib.xcodeproj */;
},
{
ProductGroup = 83848FE71807623F00E7332D /* Products */;
ProjectRef = 83848FE61807623F00E7332D /* SSEQPlayer.xcodeproj */;
},
);
projectRoot = "";
targets = (
@ -362,6 +395,13 @@
remoteRef = 8360EF4317F92C92005208A4 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
83848FEC1807624000E7332D /* SSEQPlayer.framework */ = {
isa = PBXReferenceProxy;
fileType = wrapper.framework;
path = SSEQPlayer.framework;
remoteRef = 83848FEB1807624000E7332D /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
@ -412,6 +452,11 @@
name = HighlyAdvanced;
targetProxy = 834379A317F97E9C00584396 /* PBXContainerItemProxy */;
};
83849049180764AC00E7332D /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = SSEQPlayer;
targetProxy = 83849048180764AC00E7332D /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
@ -432,7 +477,7 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_ARC = NO;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
@ -457,7 +502,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.6;
MACOSX_DEPLOYMENT_TARGET = 10.7;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
@ -469,7 +514,7 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_ARC = NO;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
@ -489,7 +534,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.6;
MACOSX_DEPLOYMENT_TARGET = 10.7;
SDKROOT = macosx;
};
name = Release;
@ -497,7 +542,6 @@
8360EEF717F92AC8005208A4 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_CXX_LIBRARY = "libstdc++";
CLANG_ENABLE_OBJC_ARC = NO;
COMBINE_HIDPI_IMAGES = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
@ -508,8 +552,6 @@
HAVE_STDINT_H,
);
INFOPLIST_FILE = "HighlyComplete/HighlyComplete-Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
MACOSX_DEPLOYMENT_TARGET = 10.6;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
WRAPPER_EXTENSION = bundle;
@ -519,7 +561,6 @@
8360EEF817F92AC8005208A4 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_CXX_LIBRARY = "libstdc++";
CLANG_ENABLE_OBJC_ARC = NO;
COMBINE_HIDPI_IMAGES = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
@ -529,8 +570,6 @@
EMU_LITTLE_ENDIAN,
);
INFOPLIST_FILE = "HighlyComplete/HighlyComplete-Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
MACOSX_DEPLOYMENT_TARGET = 10.6;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
WRAPPER_EXTENSION = bundle;

View File

@ -32,6 +32,7 @@
long totalFrames;
long framesRead;
long framesLength;
}
@end

View File

@ -24,6 +24,10 @@
#import <HighlyAdvanced/GBA.h>
#include <vector>
#import <SSEQPlayer/SDAT.h>
#import <SSEQPlayer/Player.h>
@interface psf_file_container : NSObject {
NSLock * lock;
NSMutableDictionary * list;
@ -168,9 +172,9 @@ static psf_file_callbacks source_callbacks =
return metadataList;
}
- (long)retrieveTotalFrames
- (long)retrieveFrameCount:(long)ms
{
return (tagLengthMs + tagFadeMs) * (sampleRate / 100) / 10;
return ms * (sampleRate / 100) / 10;
}
struct psf_info_meta_state
@ -623,6 +627,40 @@ struct gsf_sound_out : public GBASoundOut
}
};
struct ncsf_loader_state
{
uint32_t sseq;
std::vector<uint8_t> sdatData;
std::unique_ptr<SDAT> sdat;
ncsf_loader_state() : sseq( 0 ) { }
};
int ncsf_loader(void * context, const uint8_t * exe, size_t exe_size,
const uint8_t * reserved, size_t reserved_size)
{
struct ncsf_loader_state * state = ( struct ncsf_loader_state * ) context;
if ( reserved_size >= 4 )
{
state->sseq = get_le32( reserved );
}
if ( exe_size >= 12 )
{
uint32_t sdat_size = get_le32( exe + 8 );
if ( sdat_size > exe_size ) return -1;
if ( state->sdatData.empty() )
state->sdatData.resize( sdat_size, 0 );
else if ( state->sdatData.size() < sdat_size )
state->sdatData.resize( sdat_size );
memcpy( &state->sdatData[0], exe, sdat_size );
}
return 0;
}
- (BOOL)initializeDecoder
{
if ( type == 1 )
@ -721,6 +759,34 @@ struct gsf_sound_out : public GBASoundOut
CPUInit( system );
CPUReset( system );
}
else if ( type == 0x25 )
{
struct ncsf_loader_state * state = new struct ncsf_loader_state;
if ( psf_load( [currentUrl UTF8String], &source_callbacks, 0x25, ncsf_loader, state, 0, 0) <= 0 )
{
delete state;
return NO;
}
Player * player = new Player;
player->interpolation = INTERPOLATION_LANCZOS;
PseudoFile file;
file.data = &state->sdatData;
state->sdat.reset(new SDAT(file, state->sseq));
auto * sseqToPlay = state->sdat->sseq.get();
player->sampleRate = 44100;
player->Setup( sseqToPlay );
player->Timer();
emulatorCore = ( uint8_t * ) player;
emulatorExtra = state;
}
else if ( type == 0x41 )
{
struct qsf_loader_state * state = ( struct qsf_loader_state * ) calloc( 1, sizeof( *state ) );
@ -792,6 +858,11 @@ struct gsf_sound_out : public GBASoundOut
tagLengthMs = info.tag_length_ms;
tagFadeMs = info.tag_fade_ms;
if (!tagLengthMs) {
tagLengthMs = ( 2 * 60 + 30 ) * 1000;
tagFadeMs = 8000;
}
replayGainAlbumGain = info.albumGain;
replayGainAlbumPeak = info.albumPeak;
@ -802,7 +873,8 @@ struct gsf_sound_out : public GBASoundOut
metadataList = info.info;
framesRead = 0;
totalFrames = [self retrieveTotalFrames];
framesLength = [self retrieveFrameCount:tagLengthMs];
totalFrames = [self retrieveFrameCount:tagLengthMs + tagFadeMs];
[self willChangeValueForKey:@"properties"];
[self didChangeValueForKey:@"properties"];
@ -855,12 +927,40 @@ struct gsf_sound_out : public GBASoundOut
}
sound_out->samples_written = frames_rendered;
}
else if ( type == 0x25 )
{
size_t buffer_size = frames * sizeof(int16_t) * 2;
std::vector<uint8_t> buffer;
buffer.resize( buffer_size );
Player * player = ( Player * ) emulatorCore;
player->GenerateSamples(buffer, 0, frames);
memcpy( buf, &buffer[0], buffer_size );
}
else if ( type == 0x41 )
{
uint32_t howmany = frames;
qsound_execute( emulatorCore, 0x7fffffff, ( int16_t * ) buf, &howmany);
frames = howmany;
}
if ( framesRead >= framesLength ) {
long fadeStart = framesRead;
long fadeEnd = framesRead + frames;
long fadeTotal = totalFrames - framesLength;
long fadePos;
int16_t * buf16 = ( int16_t * ) buf;
for (fadePos = fadeStart; fadePos < fadeEnd && fadePos < totalFrames; ++fadePos) {
long scale = totalFrames - fadePos;
buf16[ 0 ] = buf16[ 0 ] * scale / fadeTotal;
buf16[ 1 ] = buf16[ 1 ] * scale / fadeTotal;
buf16 += 2;
}
if (fadePos < fadeEnd)
frames = (int)(fadePos - fadeStart);
}
framesRead += frames;
@ -875,6 +975,9 @@ struct gsf_sound_out : public GBASoundOut
CPUCleanUp( system );
soundShutdown( system );
delete system;
} else if ( type == 0x25 ) {
Player * player = ( Player * ) emulatorCore;
delete player;
} else {
free( emulatorCore );
}
@ -887,6 +990,10 @@ struct gsf_sound_out : public GBASoundOut
} else if ( type == 0x22 && emulatorExtra ) {
delete ( gsf_sound_out * ) emulatorExtra;
emulatorExtra = nil;
} else if ( type == 0x25 && emulatorExtra ) {
struct ncsf_loader_state * state = ( struct ncsf_loader_state * ) emulatorExtra;
delete state;
emulatorExtra = nil;
} else if ( type == 0x41 && emulatorExtra ) {
struct qsf_loader_state * state = ( struct qsf_loader_state * ) emulatorExtra;
free( state->key );
@ -950,6 +1057,25 @@ struct gsf_sound_out : public GBASoundOut
CPULoop( system, 250000 );
} while ( frames_to_run );
}
else if ( type == 0x25 )
{
std::vector<uint8_t> buffer;
Player * player = ( Player * ) emulatorCore;
buffer.resize(1024 * sizeof(int16_t) * 2);
long frames_to_run = frame - framesRead;
while ( frames_to_run )
{
int frames_to_render = 1024;
if ( frames_to_render > frames_to_run ) frames_to_render = (int)frames_to_run;
player->GenerateSamples(buffer, 0, frames_to_render);
frames_to_run -= frames_to_render;
}
}
else if ( type == 0x41 )
{
uint32_t howmany = (uint32_t)(frame - framesRead);
@ -957,6 +1083,8 @@ struct gsf_sound_out : public GBASoundOut
frame = (long)(howmany + framesRead);
}
framesRead = frame;
return frame;
}
@ -997,7 +1125,7 @@ struct gsf_sound_out : public GBASoundOut
+ (NSArray *)fileTypes
{
return [NSArray arrayWithObjects:@"psf",@"minipsf",@"psf2", @"minipsf2", @"ssf", @"minissf", @"dsf", @"minidsf", @"qsf", @"miniqsf", @"gsf", @"minigsf", nil];
return [NSArray arrayWithObjects:@"psf",@"minipsf",@"psf2", @"minipsf2", @"ssf", @"minissf", @"dsf", @"minidsf", @"qsf", @"miniqsf", @"gsf", @"minigsf", @"ncsf", @"minincsf", nil];
}
+ (NSArray *)mimeTypes

View File

@ -9,7 +9,7 @@
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>NoWork-Inc.${PRODUCT_NAME:rfc1034identifier}</string>
<string>org.cogx.highlycomplete</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>