From cee6ab4b8f46a9c9220a6b5ab66b9ac602def0de Mon Sep 17 00:00:00 2001 From: Chris Moeller Date: Thu, 10 Oct 2013 17:13:18 -0700 Subject: [PATCH] Changed more bundle identifiers; Added NCSF player to HighlyComplete, which necessitates using libc++, forcing this plugin to require 10.7 --- .../HighlyAdvanced.xcodeproj/project.pbxproj | 4 +- .../HighlyAdvanced/HighlyAdvanced-Info.plist | 2 +- .../project.pbxproj | 6 +- .../HighlyExperimental-Info.plist | 2 +- .../HighlyQuixotic.xcodeproj/project.pbxproj | 4 +- .../HighlyQuixotic/HighlyQuixotic-Info.plist | 2 +- .../project.pbxproj | 4 +- .../HighlyTheoretical-Info.plist | 2 +- .../SSEQPlayer.xcodeproj/project.pbxproj | 446 ++++++++++ Frameworks/SSEQPlayer/SSEQPlayer/BigSString.h | 220 +++++ Frameworks/SSEQPlayer/SSEQPlayer/Channel.cpp | 772 ++++++++++++++++++ Frameworks/SSEQPlayer/SSEQPlayer/Channel.h | 169 ++++ .../SSEQPlayer/SSEQPlayer/ConvertUTF.cpp | 602 ++++++++++++++ Frameworks/SSEQPlayer/SSEQPlayer/ConvertUTF.h | 134 +++ .../SSEQPlayer/SSEQPlayer/FATSection.cpp | 40 + Frameworks/SSEQPlayer/SSEQPlayer/FATSection.h | 33 + .../SSEQPlayer/SSEQPlayer/INFOEntry.cpp | 48 ++ Frameworks/SSEQPlayer/SSEQPlayer/INFOEntry.h | 54 ++ .../SSEQPlayer/SSEQPlayer/INFOSection.cpp | 60 ++ .../SSEQPlayer/SSEQPlayer/INFOSection.h | 37 + .../SSEQPlayer/SSEQPlayer/NDSStdHeader.cpp | 31 + .../SSEQPlayer/SSEQPlayer/NDSStdHeader.h | 26 + Frameworks/SSEQPlayer/SSEQPlayer/Player.cpp | 240 ++++++ Frameworks/SSEQPlayer/SSEQPlayer/Player.h | 54 ++ Frameworks/SSEQPlayer/SSEQPlayer/SBNK.cpp | 122 +++ Frameworks/SSEQPlayer/SSEQPlayer/SBNK.h | 61 ++ Frameworks/SSEQPlayer/SSEQPlayer/SDAT.cpp | 93 +++ Frameworks/SSEQPlayer/SSEQPlayer/SDAT.h | 31 + Frameworks/SSEQPlayer/SSEQPlayer/SSEQ.cpp | 49 ++ Frameworks/SSEQPlayer/SSEQPlayer/SSEQ.h | 32 + .../SSEQPlayer/SSEQPlayer-Info.plist | 30 + Frameworks/SSEQPlayer/SSEQPlayer/SWAR.cpp | 41 + Frameworks/SSEQPlayer/SSEQPlayer/SWAR.h | 33 + Frameworks/SSEQPlayer/SSEQPlayer/SWAV.cpp | 123 +++ Frameworks/SSEQPlayer/SSEQPlayer/SWAV.h | 32 + .../SSEQPlayer/SSEQPlayer/SYMBSection.cpp | 59 ++ .../SSEQPlayer/SSEQPlayer/SYMBSection.h | 39 + Frameworks/SSEQPlayer/SSEQPlayer/Track.cpp | 552 +++++++++++++ Frameworks/SSEQPlayer/SSEQPlayer/Track.h | 62 ++ .../SSEQPlayer/SSEQPlayer/UTFEncodeDecode.cpp | 29 + .../SSEQPlayer/SSEQPlayer/UTFEncodeDecode.h | 63 ++ .../SSEQPlayer/SSEQPlayer/UtfConverter.cpp | 69 ++ .../SSEQPlayer/SSEQPlayer/UtfConverter.h | 15 + Frameworks/SSEQPlayer/SSEQPlayer/common.h | 241 ++++++ Frameworks/SSEQPlayer/SSEQPlayer/consts.h | 65 ++ Frameworks/SSEQPlayer/SSEQPlayer/convert.h | 156 ++++ .../SSEQPlayer/en.lproj/InfoPlist.strings | 2 + .../psflib/psflib.xcodeproj/project.pbxproj | 4 +- Frameworks/psflib/psflib/psflib-Info.plist | 2 +- Info.plist | 15 + .../HighlyComplete.xcodeproj/project.pbxproj | 59 +- .../HighlyComplete/HighlyComplete/HCDecoder.h | 1 + .../HighlyComplete/HCDecoder.mm | 136 ++- .../HighlyComplete/HighlyComplete-Info.plist | 2 +- 54 files changed, 5178 insertions(+), 32 deletions(-) create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer.xcodeproj/project.pbxproj create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/BigSString.h create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/Channel.cpp create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/Channel.h create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/ConvertUTF.cpp create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/ConvertUTF.h create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/FATSection.cpp create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/FATSection.h create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/INFOEntry.cpp create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/INFOEntry.h create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/INFOSection.cpp create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/INFOSection.h create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/NDSStdHeader.cpp create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/NDSStdHeader.h create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/Player.cpp create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/Player.h create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/SBNK.cpp create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/SBNK.h create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/SDAT.cpp create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/SDAT.h create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/SSEQ.cpp create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/SSEQ.h create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/SSEQPlayer-Info.plist create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/SWAR.cpp create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/SWAR.h create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/SWAV.cpp create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/SWAV.h create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/SYMBSection.cpp create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/SYMBSection.h create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/Track.cpp create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/Track.h create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/UTFEncodeDecode.cpp create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/UTFEncodeDecode.h create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/UtfConverter.cpp create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/UtfConverter.h create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/common.h create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/consts.h create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/convert.h create mode 100644 Frameworks/SSEQPlayer/SSEQPlayer/en.lproj/InfoPlist.strings diff --git a/Frameworks/HighlyAdvanced/HighlyAdvanced.xcodeproj/project.pbxproj b/Frameworks/HighlyAdvanced/HighlyAdvanced.xcodeproj/project.pbxproj index ea3adbca1..ada591d26 100644 --- a/Frameworks/HighlyAdvanced/HighlyAdvanced.xcodeproj/project.pbxproj +++ b/Frameworks/HighlyAdvanced/HighlyAdvanced.xcodeproj/project.pbxproj @@ -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; diff --git a/Frameworks/HighlyAdvanced/HighlyAdvanced/HighlyAdvanced-Info.plist b/Frameworks/HighlyAdvanced/HighlyAdvanced/HighlyAdvanced-Info.plist index 02c2a8310..187cd6eea 100644 --- a/Frameworks/HighlyAdvanced/HighlyAdvanced/HighlyAdvanced-Info.plist +++ b/Frameworks/HighlyAdvanced/HighlyAdvanced/HighlyAdvanced-Info.plist @@ -9,7 +9,7 @@ CFBundleIconFile CFBundleIdentifier - NoWork-Inc.${PRODUCT_NAME:rfc1034identifier} + org.nowork.highlyadvanced CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/Frameworks/HighlyExperimental/HighlyExperimental.xcodeproj/project.pbxproj b/Frameworks/HighlyExperimental/HighlyExperimental.xcodeproj/project.pbxproj index 2cc2ee6fe..5ebdbaea4 100644 --- a/Frameworks/HighlyExperimental/HighlyExperimental.xcodeproj/project.pbxproj +++ b/Frameworks/HighlyExperimental/HighlyExperimental.xcodeproj/project.pbxproj @@ -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; diff --git a/Frameworks/HighlyExperimental/HighlyExperimental/HighlyExperimental-Info.plist b/Frameworks/HighlyExperimental/HighlyExperimental/HighlyExperimental-Info.plist index 02c2a8310..54b5dd836 100644 --- a/Frameworks/HighlyExperimental/HighlyExperimental/HighlyExperimental-Info.plist +++ b/Frameworks/HighlyExperimental/HighlyExperimental/HighlyExperimental-Info.plist @@ -9,7 +9,7 @@ CFBundleIconFile CFBundleIdentifier - NoWork-Inc.${PRODUCT_NAME:rfc1034identifier} + org.nowork.highlyexperimental CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/Frameworks/HighlyQuixotic/HighlyQuixotic.xcodeproj/project.pbxproj b/Frameworks/HighlyQuixotic/HighlyQuixotic.xcodeproj/project.pbxproj index a7c643d18..bfab909a5 100644 --- a/Frameworks/HighlyQuixotic/HighlyQuixotic.xcodeproj/project.pbxproj +++ b/Frameworks/HighlyQuixotic/HighlyQuixotic.xcodeproj/project.pbxproj @@ -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; }; diff --git a/Frameworks/HighlyQuixotic/HighlyQuixotic/HighlyQuixotic-Info.plist b/Frameworks/HighlyQuixotic/HighlyQuixotic/HighlyQuixotic-Info.plist index 02c2a8310..8107516ff 100644 --- a/Frameworks/HighlyQuixotic/HighlyQuixotic/HighlyQuixotic-Info.plist +++ b/Frameworks/HighlyQuixotic/HighlyQuixotic/HighlyQuixotic-Info.plist @@ -9,7 +9,7 @@ CFBundleIconFile CFBundleIdentifier - NoWork-Inc.${PRODUCT_NAME:rfc1034identifier} + org.nowork.highlyquixotic CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/Frameworks/HighlyTheoretical/HighlyTheoretical.xcodeproj/project.pbxproj b/Frameworks/HighlyTheoretical/HighlyTheoretical.xcodeproj/project.pbxproj index d8ffc44ec..ba419085b 100644 --- a/Frameworks/HighlyTheoretical/HighlyTheoretical.xcodeproj/project.pbxproj +++ b/Frameworks/HighlyTheoretical/HighlyTheoretical.xcodeproj/project.pbxproj @@ -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; diff --git a/Frameworks/HighlyTheoretical/HighlyTheoretical/HighlyTheoretical-Info.plist b/Frameworks/HighlyTheoretical/HighlyTheoretical/HighlyTheoretical-Info.plist index 02c2a8310..42541574a 100644 --- a/Frameworks/HighlyTheoretical/HighlyTheoretical/HighlyTheoretical-Info.plist +++ b/Frameworks/HighlyTheoretical/HighlyTheoretical/HighlyTheoretical-Info.plist @@ -9,7 +9,7 @@ CFBundleIconFile CFBundleIdentifier - NoWork-Inc.${PRODUCT_NAME:rfc1034identifier} + org.nowork.highlytheoretical CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/Frameworks/SSEQPlayer/SSEQPlayer.xcodeproj/project.pbxproj b/Frameworks/SSEQPlayer/SSEQPlayer.xcodeproj/project.pbxproj new file mode 100644 index 000000000..d6aa929c1 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer.xcodeproj/project.pbxproj @@ -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 = ""; }; + 83848FC51807623F00E7332D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 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 = ""; }; + 838490011807649A00E7332D /* Channel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Channel.cpp; sourceTree = ""; }; + 838490021807649A00E7332D /* Channel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Channel.h; sourceTree = ""; }; + 838490031807649A00E7332D /* common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = ""; }; + 838490041807649A00E7332D /* consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = consts.h; sourceTree = ""; }; + 838490051807649A00E7332D /* convert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = convert.h; sourceTree = ""; }; + 838490061807649A00E7332D /* ConvertUTF.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConvertUTF.cpp; sourceTree = ""; }; + 838490071807649A00E7332D /* ConvertUTF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConvertUTF.h; sourceTree = ""; }; + 838490081807649A00E7332D /* FATSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FATSection.cpp; sourceTree = ""; }; + 838490091807649A00E7332D /* FATSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FATSection.h; sourceTree = ""; }; + 8384900A1807649A00E7332D /* INFOEntry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = INFOEntry.cpp; sourceTree = ""; }; + 8384900B1807649A00E7332D /* INFOEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INFOEntry.h; sourceTree = ""; }; + 8384900C1807649A00E7332D /* INFOSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = INFOSection.cpp; sourceTree = ""; }; + 8384900D1807649A00E7332D /* INFOSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = INFOSection.h; sourceTree = ""; }; + 8384900E1807649A00E7332D /* NDSStdHeader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NDSStdHeader.cpp; sourceTree = ""; }; + 8384900F1807649A00E7332D /* NDSStdHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NDSStdHeader.h; sourceTree = ""; }; + 838490101807649A00E7332D /* Player.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Player.cpp; sourceTree = ""; }; + 838490111807649A00E7332D /* Player.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Player.h; sourceTree = ""; }; + 838490121807649A00E7332D /* SBNK.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SBNK.cpp; sourceTree = ""; }; + 838490131807649A00E7332D /* SBNK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBNK.h; sourceTree = ""; }; + 838490141807649A00E7332D /* SDAT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SDAT.cpp; sourceTree = ""; }; + 838490151807649A00E7332D /* SDAT.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDAT.h; sourceTree = ""; }; + 838490161807649A00E7332D /* SSEQ.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SSEQ.cpp; sourceTree = ""; }; + 838490171807649A00E7332D /* SSEQ.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSEQ.h; sourceTree = ""; }; + 838490181807649A00E7332D /* SWAR.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SWAR.cpp; sourceTree = ""; }; + 838490191807649A00E7332D /* SWAR.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWAR.h; sourceTree = ""; }; + 8384901A1807649A00E7332D /* SWAV.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SWAV.cpp; sourceTree = ""; }; + 8384901B1807649A00E7332D /* SWAV.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWAV.h; sourceTree = ""; }; + 8384901C1807649A00E7332D /* SYMBSection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SYMBSection.cpp; sourceTree = ""; }; + 8384901D1807649A00E7332D /* SYMBSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SYMBSection.h; sourceTree = ""; }; + 8384901E1807649A00E7332D /* Track.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Track.cpp; sourceTree = ""; }; + 8384901F1807649A00E7332D /* Track.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Track.h; sourceTree = ""; }; + 838490201807649A00E7332D /* UtfConverter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UtfConverter.cpp; sourceTree = ""; }; + 838490211807649A00E7332D /* UtfConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UtfConverter.h; sourceTree = ""; }; + 838490221807649A00E7332D /* UTFEncodeDecode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UTFEncodeDecode.cpp; sourceTree = ""; }; + 838490231807649A00E7332D /* UTFEncodeDecode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UTFEncodeDecode.h; sourceTree = ""; }; +/* 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 = ""; + }; + 83848FB91807623F00E7332D /* Products */ = { + isa = PBXGroup; + children = ( + 83848FB81807623F00E7332D /* SSEQPlayer.framework */, + ); + name = Products; + sourceTree = ""; + }; + 83848FBA1807623F00E7332D /* Frameworks */ = { + isa = PBXGroup; + children = ( + 83848FBB1807623F00E7332D /* Cocoa.framework */, + 83848FD01807623F00E7332D /* XCTest.framework */, + 83848FBD1807623F00E7332D /* Other Frameworks */, + ); + name = Frameworks; + sourceTree = ""; + }; + 83848FBD1807623F00E7332D /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + 83848FBE1807623F00E7332D /* Foundation.framework */, + 83848FBF1807623F00E7332D /* CoreData.framework */, + 83848FC01807623F00E7332D /* AppKit.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + 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 = ""; + }; + 83848FC21807623F00E7332D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 83848FC31807623F00E7332D /* SSEQPlayer-Info.plist */, + 83848FC41807623F00E7332D /* InfoPlist.strings */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* 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 = ""; + }; +/* 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 */; +} diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/BigSString.h b/Frameworks/SSEQPlayer/SSEQPlayer/BigSString.h new file mode 100644 index 000000000..d40ce8b58 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/BigSString.h @@ -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 +#include +#include +#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 diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/Channel.cpp b/Frameworks/SSEQPlayer/SSEQPlayer/Channel.cpp new file mode 100644 index 000000000..70ab361fa --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/Channel.cpp @@ -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 +#include +#include +#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 inline bool fEqual(T x, T y, int N = 1) +{ + T diff = std::abs(x - y); + T tolerance = N * std::numeric_limits::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(i) / COSINE_RESOLUTION) * M_PI)) * 0.5; + double dx = static_cast(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(this->key) - static_cast(this->orgKey)) * 64; + tune += (static_cast(trk.pitchBend) * static_cast(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(trk.portaKey) - static_cast(this->key)) << 22; + this->sweepPitch += diff >> 16; + + if (!trk.portaTime) + { + this->sweepLen = this->noteLength; + this->manualSweep = true; + } + else + { + int sq_time = static_cast(trk.portaTime) * static_cast(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(basetmr) * (static_cast(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(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(this->ampl) * static_cast(this->attackLvl)) / 255; + if (!this->ampl) + this->state = CS_DECAY; + break; + case CS_DECAY: + { + this->ampl -= static_cast(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(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(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(modParam) >> 26)) << 18); + + // Update the modulation variables + + uint16_t speed = static_cast(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(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(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(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(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(std::floor(ratio * LANCZOS_RESOLUTION)); + int step = this->reg.sampleIncrease > 1.0 ? static_cast((1.0 / this->reg.sampleIncrease) * LANCZOS_RESOLUTION) : LANCZOS_RESOLUTION; + long shift_adj = shift * step / LANCZOS_RESOLUTION; + for (; i >= -static_cast(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(LANCZOS_WIDTH * 2); ++i) + sum += data[i - static_cast(LANCZOS_WIDTH) + 1] * kernel[i]; + return static_cast(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(((((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(((((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(((c3 * ratio + c2) * ratio + c1) * ratio + c0); + } + } + else if (this->ply->interpolation == INTERPOLATION_COSINE) + return static_cast(data[0] + this->cosine_lut[static_cast(ratio * COSINE_RESOLUTION)] * (data[1] - data[0])); + else // INTERPOLATION_LINEAR + return static_cast(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(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(this->reg.samplePosition) & 0x7]; + else + { + if (this->reg.psgLastCount != static_cast(this->reg.samplePosition)) + { + uint32_t max = static_cast(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(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(this->reg.samplePosition); + uint32_t newloc = static_cast(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)); +} diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/Channel.h b/Frameworks/SSEQPlayer/SSEQPlayer/Channel.h new file mode 100644 index 000000000..0a116fec7 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/Channel.h @@ -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 +#include +#include +#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 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 diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/ConvertUTF.cpp b/Frameworks/SSEQPlayer/SSEQPlayer/ConvertUTF.cpp new file mode 100644 index 000000000..747f447b5 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/ConvertUTF.cpp @@ -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(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((ch >> halfShift) + UNI_SUR_HIGH_START); + *target++ = static_cast((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((ch | byteMark) & byteMask); ch >>= 6; + case 3: *--target = static_cast((ch | byteMark) & byteMask); ch >>= 6; + case 2: *--target = static_cast((ch | byteMark) & byteMask); ch >>= 6; + case 1: *--target = static_cast(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(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((ch >> halfShift) + UNI_SUR_HIGH_START); + *target++ = static_cast((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((ch | byteMark) & byteMask); ch >>= 6; + case 3: *--target = static_cast((ch | byteMark) & byteMask); ch >>= 6; + case 2: *--target = static_cast((ch | byteMark) & byteMask); ch >>= 6; + case 1: *--target = static_cast(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. + + --------------------------------------------------------------------- */ diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/ConvertUTF.h b/Frameworks/SSEQPlayer/SSEQPlayer/ConvertUTF.h new file mode 100644 index 000000000..c363ffa1c --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/ConvertUTF.h @@ -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: , , + or 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 diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/FATSection.cpp b/Frameworks/SSEQPlayer/SSEQPlayer/FATSection.cpp new file mode 100644 index 000000000..5062b60c2 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/FATSection.cpp @@ -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 +#include "FATSection.h" + +FATRecord::FATRecord() : offset(0) +{ +} + +void FATRecord::Read(PseudoFile &file) +{ + this->offset = file.ReadLE(); + file.ReadLE(); // 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(); // size + uint32_t count = file.ReadLE(); + this->records.resize(count); + for (uint32_t i = 0; i < count; ++i) + this->records[i].Read(file); +} diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/FATSection.h b/Frameworks/SSEQPlayer/SSEQPlayer/FATSection.h new file mode 100644 index 000000000..198cfae12 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/FATSection.h @@ -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 records; + + FATSection(); + + void Read(PseudoFile &file); +}; + +#endif diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/INFOEntry.cpp b/Frameworks/SSEQPlayer/SSEQPlayer/INFOEntry.cpp new file mode 100644 index 000000000..8bc69bdea --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/INFOEntry.cpp @@ -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(); + file.ReadLE(); // unknown + this->bank = file.ReadLE(); + this->vol = file.ReadLE(); + if (!this->vol) + this->vol = 0x7F; // Prevents nothing for volume + file.ReadLE(); // cpr + file.ReadLE(); // ppr + file.ReadLE(); // ply +} + +INFOEntryBANK::INFOEntryBANK() : fileID(0) +{ + memset(this->waveArc, 0, sizeof(this->waveArc)); +} + +void INFOEntryBANK::Read(PseudoFile &file) +{ + this->fileID = file.ReadLE(); + file.ReadLE(); // unknown + file.ReadLE(this->waveArc); +} + +INFOEntryWAVEARC::INFOEntryWAVEARC() : fileID(0) +{ +} + +void INFOEntryWAVEARC::Read(PseudoFile &file) +{ + this->fileID = file.ReadLE(); +} diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/INFOEntry.h b/Frameworks/SSEQPlayer/SSEQPlayer/INFOEntry.h new file mode 100644 index 000000000..0a4b4dd7b --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/INFOEntry.h @@ -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 diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/INFOSection.cpp b/Frameworks/SSEQPlayer/SSEQPlayer/INFOSection.cpp new file mode 100644 index 000000000..c7bffc198 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/INFOSection.cpp @@ -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 +#include "INFOSection.h" + +template INFORecord::INFORecord() : entries() +{ +} + +template void INFORecord::Read(PseudoFile &file, uint32_t startOffset) +{ + uint32_t count = file.ReadLE(); + auto entryOffsets = std::vector(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(); // 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); + } +} diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/INFOSection.h b/Frameworks/SSEQPlayer/SSEQPlayer/INFOSection.h new file mode 100644 index 000000000..a41361a46 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/INFOSection.h @@ -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 +#include "INFOEntry.h" +#include "common.h" + +template struct INFORecord +{ + std::map entries; + + INFORecord(); + + void Read(PseudoFile &file, uint32_t startOffset); +}; + +struct INFOSection +{ + INFORecord SEQrecord; + INFORecord BANKrecord; + INFORecord WAVEARCrecord; + + INFOSection(); + + void Read(PseudoFile &file); +}; + +#endif diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/NDSStdHeader.cpp b/Frameworks/SSEQPlayer/SSEQPlayer/NDSStdHeader.cpp new file mode 100644 index 000000000..f996168cc --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/NDSStdHeader.cpp @@ -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 +#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(); + file.ReadLE(); // file size + file.ReadLE(); // structure size + file.ReadLE(); // # 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"); +} diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/NDSStdHeader.h b/Frameworks/SSEQPlayer/SSEQPlayer/NDSStdHeader.h new file mode 100644 index 000000000..7b2b6077a --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/NDSStdHeader.h @@ -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 diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/Player.cpp b/Frameworks/SSEQPlayer/SSEQPlayer/Player.cpp new file mode 100644 index 000000000..19e6ba20c --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/Player.cpp @@ -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(this->tempo) * static_cast(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 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 &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; + } + } +} + diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/Player.h b/Frameworks/SSEQPlayer/SSEQPlayer/Player.h new file mode 100644 index 000000000..40b20d32d --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/Player.h @@ -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 +#include +#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 &buf, unsigned offset, unsigned samples); +}; + +#endif diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/SBNK.cpp b/Frameworks/SSEQPlayer/SSEQPlayer/SBNK.cpp new file mode 100644 index 000000000..22e7eec13 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/SBNK.cpp @@ -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(); + this->swar = file.ReadLE(); + this->noteNumber = file.ReadLE(); + this->attackRate = file.ReadLE(); + this->decayRate = file.ReadLE(); + this->sustainLevel = file.ReadLE(); + this->releaseRate = file.ReadLE(); + this->pan = file.ReadLE(); +} + +SBNKInstrument::SBNKInstrument() : record(0), ranges() +{ +} + +void SBNKInstrument::Read(PseudoFile &file, uint32_t startOffset) +{ + this->record = file.ReadLE(); + uint16_t offset = file.ReadLE(); + file.ReadLE(); + uint32_t endOfInst = file.pos; + file.pos = startOffset + offset; + if (this->record) + { + if (this->record == 16) + { + uint8_t lowNote = file.ReadLE(); + uint8_t highNote = file.ReadLE(); + uint8_t num = highNote - lowNote + 1; + for (uint8_t i = 0; i < num; ++i) + { + uint16_t thisRecord = file.ReadLE(); + 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(); + 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(); // size + uint32_t reserved[8]; + file.ReadLE(reserved); + uint32_t count = file.ReadLE(); + this->instruments.resize(count); + for (uint32_t i = 0; i < count; ++i) + this->instruments[i].Read(file, startOfSBNK); +} diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/SBNK.h b/Frameworks/SSEQPlayer/SSEQPlayer/SBNK.h new file mode 100644 index 000000000..0bdad32de --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/SBNK.h @@ -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 ranges; + + SBNKInstrument(); + + void Read(PseudoFile &file, uint32_t startOffset); +}; + +struct SBNK +{ + std::string filename; + std::vector 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 diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/SDAT.cpp b/Frameworks/SSEQPlayer/SSEQPlayer/SDAT.cpp new file mode 100644 index 000000000..89d96f483 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/SDAT.cpp @@ -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(); + file.ReadLE(); // SYMB size + uint32_t INFOOffset = file.ReadLE(); + file.ReadLE(); // INFO size + uint32_t FATOffset = file.ReadLE(); + file.ReadLE(); // 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(); + } +} diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/SDAT.h b/Frameworks/SSEQPlayer/SSEQPlayer/SDAT.h new file mode 100644 index 000000000..1f576b792 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/SDAT.h @@ -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 +#include "SSEQ.h" +#include "SBNK.h" +#include "SWAR.h" +#include "common.h" + +struct SDAT +{ + std::unique_ptr sseq; + std::unique_ptr sbnk; + std::unique_ptr swar[4]; + + SDAT(PseudoFile &file, uint32_t sseqToLoad); +private: + SDAT(const SDAT &); + SDAT &operator=(const SDAT &); +}; + +#endif diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/SSEQ.cpp b/Frameworks/SSEQPlayer/SSEQPlayer/SSEQ.cpp new file mode 100644 index 000000000..50f2a0c34 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/SSEQ.cpp @@ -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 dataOffset = file.ReadLE(); + this->data.resize(size - 12, 0); + file.pos = startOfSSEQ + dataOffset; + file.ReadLE(this->data); +} diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/SSEQ.h b/Frameworks/SSEQPlayer/SSEQPlayer/SSEQ.h new file mode 100644 index 000000000..562a1a6e8 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/SSEQ.h @@ -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 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 diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/SSEQPlayer-Info.plist b/Frameworks/SSEQPlayer/SSEQPlayer/SSEQPlayer-Info.plist new file mode 100644 index 000000000..aad93e8f2 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/SSEQPlayer-Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + org.nowork.sseqplayer + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + NSHumanReadableCopyright + Copyright © 2013 Christopher Snowhill. All rights reserved. + NSPrincipalClass + + + diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/SWAR.cpp b/Frameworks/SSEQPlayer/SSEQPlayer/SWAR.cpp new file mode 100644 index 000000000..40a98e642 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/SWAR.cpp @@ -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 +#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(); // size + uint32_t reserved[8]; + file.ReadLE(reserved); + uint32_t count = file.ReadLE(); + auto offsets = std::vector(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); + } +} diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/SWAR.h b/Frameworks/SSEQPlayer/SSEQPlayer/SWAR.h new file mode 100644 index 000000000..c149636a8 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/SWAR.h @@ -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 +#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 swavs; + + INFOEntryWAVEARC info; + + SWAR(const std::string &fn = ""); + + void Read(PseudoFile &file); +}; + +#endif diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/SWAV.cpp b/Frameworks/SSEQPlayer/SSEQPlayer/SWAV.cpp new file mode 100644 index 000000000..5c427321a --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/SWAV.cpp @@ -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 &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(); + this->loop = file.ReadLE(); + this->sampleRate = file.ReadLE(); + this->time = file.ReadLE(); + this->loopOffset = file.ReadLE(); + this->nonLoopLength = file.ReadLE(); + uint32_t size = (this->loopOffset + this->nonLoopLength) * 4; + auto origData = std::vector(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(&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]; +} diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/SWAV.h b/Frameworks/SSEQPlayer/SSEQPlayer/SWAV.h new file mode 100644 index 000000000..ad9c1454b --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/SWAV.h @@ -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 data; + const int16_t *dataptr; + + SWAV(); + + void Read(PseudoFile &file); + void DecodeADPCM(const std::vector &data); +}; + +#endif diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/SYMBSection.cpp b/Frameworks/SSEQPlayer/SSEQPlayer/SYMBSection.cpp new file mode 100644 index 000000000..13512c8c6 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/SYMBSection.cpp @@ -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 +#include "SYMBSection.h" + +SYMBRecord::SYMBRecord() : entries() +{ +} + +void SYMBRecord::Read(PseudoFile &file, uint32_t startOffset) +{ + uint32_t count = file.ReadLE(); + auto entryOffsets = std::vector(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(); // 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); + } +} diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/SYMBSection.h b/Frameworks/SSEQPlayer/SSEQPlayer/SYMBSection.h new file mode 100644 index 000000000..62d33851f --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/SYMBSection.h @@ -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 +#include "common.h" + +struct SYMBRecord +{ + std::map 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 diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/Track.cpp b/Frameworks/SSEQPlayer/SSEQPlayer/Track.cpp new file mode 100644 index 000000000..0188d789f --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/Track.cpp @@ -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(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; + } + } +} diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/Track.h b/Frameworks/SSEQPlayer/SSEQPlayer/Track.h new file mode 100644 index 000000000..c209cd063 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/Track.h @@ -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 +#include "consts.h" + +struct Player; + +struct Track +{ + int8_t trackId; + + std::bitset 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 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 diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/UTFEncodeDecode.cpp b/Frameworks/SSEQPlayer/SSEQPlayer/UTFEncodeDecode.cpp new file mode 100644 index 000000000..663006ad6 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/UTFEncodeDecode.cpp @@ -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 ""; + } +} diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/UTFEncodeDecode.h b/Frameworks/SSEQPlayer/SSEQPlayer/UTFEncodeDecode.h new file mode 100644 index 000000000..f796b4cc4 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/UTFEncodeDecode.h @@ -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 +#include +#include +#include + +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 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(in); + } + inline std::string narrow(const std::wstring &in) + { + return this->convert(in); + } +private: + typedef std::codecvt 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 std::basic_string convert(const std::basic_string &in) + { + auto &facet = std::use_facet(this->loc); + std::basic_stringstream 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(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 diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/UtfConverter.cpp b/Frameworks/SSEQPlayer/SSEQPlayer/UtfConverter.cpp new file mode 100644 index 000000000..e00e88719 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/UtfConverter.cpp @@ -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 +#include +#include +#include "ConvertUTF.h" + +namespace UtfConverter +{ + std::wstring FromUtf8(const std::string &utf8string) + { + auto widesize = utf8string.length(); + auto result = std::vector(widesize + 1, L'\0'); + auto orig = std::vector(widesize + 1, '\0'); + std::copy(utf8string.begin(), utf8string.end(), orig.begin()); + auto *sourcestart = reinterpret_cast(&orig[0]), *sourceend = sourcestart + widesize; + ConversionResult res; + if (sizeof(wchar_t) == 2) + { + auto *targetstart = reinterpret_cast(&result[0]), *targetend = targetstart + widesize; + res = ConvertUTF8toUTF16(&sourcestart, sourceend, &targetstart, targetend, strictConversion); + *targetstart = 0; + unsigned end = targetstart - reinterpret_cast(&result[0]); + result.erase(result.begin() + end, result.end()); + } + else if (sizeof(wchar_t) == 4) + { + auto *targetstart = reinterpret_cast(&result[0]), *targetend = targetstart + widesize; + res = ConvertUTF8toUTF32(&sourcestart, sourceend, &targetstart, targetend, strictConversion); + *targetstart = 0; + unsigned end = targetstart - reinterpret_cast(&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(utf8size, '\0'); + auto orig = std::vector(widesize + 1, L'\0'); + std::copy(widestring.begin(), widestring.end(), orig.begin()); + auto *targetstart = reinterpret_cast(&result[0]), *targetend = targetstart + utf8size; + ConversionResult res; + if (sizeof(wchar_t) == 2) + { + auto *sourcestart = reinterpret_cast(&orig[0]), *sourceend = sourcestart + widesize; + res = ConvertUTF16toUTF8(&sourcestart, sourceend, &targetstart, targetend, strictConversion); + } + else if (sizeof(wchar_t) == 4) + { + auto *sourcestart = reinterpret_cast(&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(&result[0]); + result.erase(result.begin() + end, result.end()); + return std::string(result.begin(), result.end()); + } +} diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/UtfConverter.h b/Frameworks/SSEQPlayer/SSEQPlayer/UtfConverter.h new file mode 100644 index 000000000..92e423b53 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/UtfConverter.h @@ -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 + +namespace UtfConverter +{ + std::wstring FromUtf8(const std::string &); + std::string ToUtf8(const std::wstring &); +} + +#endif diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/common.h b/Frameworks/SSEQPlayer/SSEQPlayer/common.h new file mode 100644 index 000000000..2c3437f76 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/common.h @@ -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 +#include +#include +#include + +/* + * Pseudo-file data structure + */ + +struct PseudoFile +{ + std::vector *data; + uint32_t pos; + + PseudoFile() : data(nullptr), pos(0) + { + } + + template T ReadLE() + { + T finalVal = 0; + for (size_t i = 0; i < sizeof(T); ++i) + finalVal |= (*this->data)[this->pos++] << (i * 8); + return finalVal; + } + + template void ReadLE(T (&arr)[N]) + { + for (size_t i = 0; i < N; ++i) + arr[i] = this->ReadLE(); + } + + template void ReadLE( uint8_t arr[N]) + { + memcpy(&arr[0], &(*this->data)[this->pos], N); + this->pos += N; + } + + template void ReadLE(std::vector &arr) + { + for (size_t i = 0, len = arr.size(); i < len; ++i) + arr[i] = this->ReadLE(); + } + + void ReadLE(std::vector &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(this->ReadLE()); + 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 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 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(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 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 diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/consts.h b/Frameworks/SSEQPlayer/SSEQPlayer/consts.h new file mode 100644 index 000000000..364b51856 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/consts.h @@ -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 + +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 diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/convert.h b/Frameworks/SSEQPlayer/SSEQPlayer/convert.h new file mode 100644 index 000000000..0c18d1994 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/convert.h @@ -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 +#include +#include +#include +#include +#include +#include +#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 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 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 inline void convert(const std::basic_string &s, T &x, bool failIfLeftoverChars = true) +{ + std::basic_istringstream i(s); + S c; + if (!(i >> x) || (failIfLeftoverChars && i.get(c))) + throw BadConversion(std::string("convert(") + typeid(S).name() + ")"); +} + +template inline T convertTo(const std::basic_string &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(input.begin(), input.end()); + size_t length = inputChars.size(); + auto masks = std::vector::mask>(length); + std::use_facet>(loc).is(&inputChars[0], &inputChars[length], &masks[0]); + for (size_t x = 0; x < length; ++x) + if (inputChars[x] != '.' && !(masks[x] & std::ctype::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(hoursStr, false); + } + if (!minutesStr.empty()) + { + if (!ConvertFuncs::IsDigitsOnly(minutesStr)) + return 0; + minutes = convertTo(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(secondsStr, false); + } + seconds += minutes * 60 + hours * 1440; + return static_cast(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(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 diff --git a/Frameworks/SSEQPlayer/SSEQPlayer/en.lproj/InfoPlist.strings b/Frameworks/SSEQPlayer/SSEQPlayer/en.lproj/InfoPlist.strings new file mode 100644 index 000000000..477b28ff8 --- /dev/null +++ b/Frameworks/SSEQPlayer/SSEQPlayer/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Frameworks/psflib/psflib.xcodeproj/project.pbxproj b/Frameworks/psflib/psflib.xcodeproj/project.pbxproj index 3f30ba2b0..00d96edfd 100644 --- a/Frameworks/psflib/psflib.xcodeproj/project.pbxproj +++ b/Frameworks/psflib/psflib.xcodeproj/project.pbxproj @@ -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; diff --git a/Frameworks/psflib/psflib/psflib-Info.plist b/Frameworks/psflib/psflib/psflib-Info.plist index 02c2a8310..870ea1afa 100644 --- a/Frameworks/psflib/psflib/psflib-Info.plist +++ b/Frameworks/psflib/psflib/psflib-Info.plist @@ -9,7 +9,7 @@ CFBundleIconFile CFBundleIdentifier - NoWork-Inc.${PRODUCT_NAME:rfc1034identifier} + org.nowork.psflib CFBundleInfoDictionaryVersion 6.0 CFBundleName diff --git a/Info.plist b/Info.plist index 028a6ae4a..c3db9d716 100644 --- a/Info.plist +++ b/Info.plist @@ -24,6 +24,21 @@ CFBundleTypeRole None + + CFBundleTypeExtensions + + minincsf + ncsf + + LSTypeIsPackage + + CFBundleTypeRole + Viewer + CFBundleTypeIconFile + vg.icns + CFBundleTypeName + Nitro Sound Composer Audio File + CFBundleTypeExtensions diff --git a/Plugins/HighlyComplete/HighlyComplete.xcodeproj/project.pbxproj b/Plugins/HighlyComplete/HighlyComplete.xcodeproj/project.pbxproj index c74ffd215..aaf028062 100644 --- a/Plugins/HighlyComplete/HighlyComplete.xcodeproj/project.pbxproj +++ b/Plugins/HighlyComplete/HighlyComplete.xcodeproj/project.pbxproj @@ -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 = ""; }; 8360EEF317F92AC8005208A4 /* HighlyComplete-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "HighlyComplete-Prefix.pch"; sourceTree = ""; }; 8360EF3E17F92C91005208A4 /* HighlyExperimental.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = HighlyExperimental.xcodeproj; path = ../../Frameworks/HighlyExperimental/HighlyExperimental.xcodeproj; sourceTree = ""; }; + 83848FE61807623F00E7332D /* SSEQPlayer.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SSEQPlayer.xcodeproj; path = ../../Frameworks/SSEQPlayer/SSEQPlayer.xcodeproj; sourceTree = ""; }; /* 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 = ""; @@ -252,6 +272,14 @@ name = Products; sourceTree = ""; }; + 83848FE71807623F00E7332D /* Products */ = { + isa = PBXGroup; + children = ( + 83848FEC1807624000E7332D /* SSEQPlayer.framework */, + ); + name = Products; + sourceTree = ""; + }; /* 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; diff --git a/Plugins/HighlyComplete/HighlyComplete/HCDecoder.h b/Plugins/HighlyComplete/HighlyComplete/HCDecoder.h index e42256ea1..b6ec1baa6 100644 --- a/Plugins/HighlyComplete/HighlyComplete/HCDecoder.h +++ b/Plugins/HighlyComplete/HighlyComplete/HCDecoder.h @@ -32,6 +32,7 @@ long totalFrames; long framesRead; + long framesLength; } @end diff --git a/Plugins/HighlyComplete/HighlyComplete/HCDecoder.mm b/Plugins/HighlyComplete/HighlyComplete/HCDecoder.mm index d6d0fd8d7..c065cb4fe 100644 --- a/Plugins/HighlyComplete/HighlyComplete/HCDecoder.mm +++ b/Plugins/HighlyComplete/HighlyComplete/HCDecoder.mm @@ -24,6 +24,10 @@ #import +#include +#import +#import + @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 sdatData; + std::unique_ptr 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 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 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 diff --git a/Plugins/HighlyComplete/HighlyComplete/HighlyComplete-Info.plist b/Plugins/HighlyComplete/HighlyComplete/HighlyComplete-Info.plist index 9d8051c76..44af57313 100644 --- a/Plugins/HighlyComplete/HighlyComplete/HighlyComplete-Info.plist +++ b/Plugins/HighlyComplete/HighlyComplete/HighlyComplete-Info.plist @@ -9,7 +9,7 @@ CFBundleIconFile CFBundleIdentifier - NoWork-Inc.${PRODUCT_NAME:rfc1034identifier} + org.cogx.highlycomplete CFBundleInfoDictionaryVersion 6.0 CFBundleName