Changed more bundle identifiers; Added NCSF player to HighlyComplete, which necessitates using libc++, forcing this plugin to require 10.7
parent
dda7bf994d
commit
cee6ab4b8f
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 */;
|
||||
}
|
|
@ -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
|
|
@ -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));
|
||||
}
|
|
@ -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
|
|
@ -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.
|
||||
|
||||
--------------------------------------------------------------------- */
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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>();
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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");
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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];
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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 "";
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
/* Localized versions of Info.plist keys */
|
||||
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
15
Info.plist
15
Info.plist
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
long totalFrames;
|
||||
long framesRead;
|
||||
long framesLength;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue